forked from upstream-mirrors/OpenTracks
Releasing version 1.0.19 (bugfix release)
This commit is contained in:
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* 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
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.util.MyTracksUtils;
|
||||
import com.google.android.maps.GeoPoint;
|
||||
import com.google.android.maps.MapView;
|
||||
import com.google.android.maps.Overlay;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Canvas;
|
||||
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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A map overlay that displays a "MyLocation" arrow, an error circle, the
|
||||
* currently recording track and optionally a selected track.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class MyTracksOverlay extends Overlay {
|
||||
|
||||
private final Drawable arrow[] = new Drawable[18];
|
||||
private final int arrowWidth, arrowHeight;
|
||||
private final Drawable statsMarker;
|
||||
private final Drawable waypointMarker;
|
||||
private final Drawable startMarker;
|
||||
private final Drawable endMarker;
|
||||
private final int markerWidth, markerHeight;
|
||||
private final Paint selectedTrackPaint;
|
||||
private final Paint errorCirclePaint;
|
||||
private final Context context;
|
||||
private final ArrayList<Waypoint> waypoints = new ArrayList<Waypoint>();
|
||||
private final ArrayList<Location> points = new ArrayList<Location>(1024);
|
||||
|
||||
private boolean trackDrawingEnabled;
|
||||
private int lastHeading = 0;
|
||||
private Location myLocation;
|
||||
private boolean showEndMarker = true;
|
||||
private boolean drawBounds;
|
||||
|
||||
public MyTracksOverlay(Context context) {
|
||||
this.context = context;
|
||||
|
||||
// TODO: Can we use a FrameAnimation or similar here rather
|
||||
// than individual resources for each arrow direction?
|
||||
|
||||
arrow[0] = context.getResources().getDrawable(R.drawable.arrow_0);
|
||||
arrow[1] = context.getResources().getDrawable(R.drawable.arrow_20);
|
||||
arrow[2] = context.getResources().getDrawable(R.drawable.arrow_40);
|
||||
arrow[3] = context.getResources().getDrawable(R.drawable.arrow_60);
|
||||
arrow[4] = context.getResources().getDrawable(R.drawable.arrow_80);
|
||||
arrow[5] = context.getResources().getDrawable(R.drawable.arrow_100);
|
||||
arrow[6] = context.getResources().getDrawable(R.drawable.arrow_120);
|
||||
arrow[7] = context.getResources().getDrawable(R.drawable.arrow_140);
|
||||
arrow[8] = context.getResources().getDrawable(R.drawable.arrow_160);
|
||||
arrow[9] = context.getResources().getDrawable(R.drawable.arrow_180);
|
||||
arrow[10] = context.getResources().getDrawable(R.drawable.arrow_200);
|
||||
arrow[11] = context.getResources().getDrawable(R.drawable.arrow_220);
|
||||
arrow[12] = context.getResources().getDrawable(R.drawable.arrow_240);
|
||||
arrow[13] = context.getResources().getDrawable(R.drawable.arrow_260);
|
||||
arrow[14] = context.getResources().getDrawable(R.drawable.arrow_280);
|
||||
arrow[15] = context.getResources().getDrawable(R.drawable.arrow_300);
|
||||
arrow[16] = context.getResources().getDrawable(R.drawable.arrow_320);
|
||||
arrow[17] = context.getResources().getDrawable(R.drawable.arrow_340);
|
||||
arrowWidth = arrow[lastHeading].getIntrinsicWidth();
|
||||
arrowHeight = arrow[lastHeading].getIntrinsicHeight();
|
||||
for (int i = 0; i <= 17; i++) {
|
||||
arrow[i].setBounds(0, 0, arrowWidth, arrowHeight);
|
||||
}
|
||||
|
||||
statsMarker = context.getResources().getDrawable(R.drawable.ylw_pushpin);
|
||||
markerWidth = statsMarker.getIntrinsicWidth();
|
||||
markerHeight = statsMarker.getIntrinsicHeight();
|
||||
statsMarker.setBounds(0, 0, markerWidth, markerHeight);
|
||||
|
||||
startMarker = context.getResources().getDrawable(R.drawable.green_dot);
|
||||
startMarker.setBounds(0, 0, markerWidth, markerHeight);
|
||||
|
||||
endMarker = context.getResources().getDrawable(R.drawable.red_dot);
|
||||
endMarker.setBounds(0, 0, markerWidth, markerHeight);
|
||||
|
||||
waypointMarker =
|
||||
context.getResources().getDrawable(R.drawable.blue_pushpin);
|
||||
waypointMarker.setBounds(0, 0, markerWidth, markerHeight);
|
||||
|
||||
selectedTrackPaint = new Paint();
|
||||
selectedTrackPaint.setColor(context.getResources().getColor(R.color.red));
|
||||
selectedTrackPaint.setStrokeWidth(3);
|
||||
selectedTrackPaint.setStyle(Paint.Style.STROKE);
|
||||
selectedTrackPaint.setAntiAlias(true);
|
||||
|
||||
errorCirclePaint = new Paint();
|
||||
errorCirclePaint.setColor(context.getResources().getColor(R.color.blue));
|
||||
errorCirclePaint.setStyle(Paint.Style.STROKE);
|
||||
errorCirclePaint.setStrokeWidth(3);
|
||||
errorCirclePaint.setAlpha(127);
|
||||
errorCirclePaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a location to the map overlay.
|
||||
*
|
||||
* NOTE: this method takes ownership of this location and may change it.
|
||||
*
|
||||
* @param l the location to add
|
||||
*/
|
||||
public void addLocation(Location l) {
|
||||
if (l != null) {
|
||||
synchronized (points) {
|
||||
points.add(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addWaypoint(Waypoint wpt) {
|
||||
waypoints.add(wpt);
|
||||
}
|
||||
|
||||
public int getNumLocations() {
|
||||
synchronized (points) {
|
||||
return points.size();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearWaypoints() {
|
||||
waypoints.clear();
|
||||
}
|
||||
|
||||
public void clearPoints() {
|
||||
synchronized (points) {
|
||||
points.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void setTrackDrawingEnabled(boolean trackDrawingEnabled) {
|
||||
this.trackDrawingEnabled = trackDrawingEnabled;
|
||||
}
|
||||
|
||||
public void setShowEndMarker(boolean showEndMarker) {
|
||||
this.showEndMarker = showEndMarker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
|
||||
if (shadow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (trackDrawingEnabled) {
|
||||
// Draw the selected track:
|
||||
drawTrack(canvas, mapView);
|
||||
|
||||
// Draw the waypoints:
|
||||
drawWaypoints(canvas, mapView);
|
||||
}
|
||||
|
||||
// Draw the current location
|
||||
drawMyLocation(canvas, mapView);
|
||||
}
|
||||
|
||||
private void drawWaypoints(Canvas canvas, MapView mapView) {
|
||||
ArrayList<Waypoint> currentWaypoints = waypoints;
|
||||
for (int i = 1; i < currentWaypoints.size(); i++) {
|
||||
Waypoint wpt = currentWaypoints.get(i);
|
||||
if (wpt == null) {
|
||||
continue;
|
||||
}
|
||||
Location loc = wpt.getLocation();
|
||||
if (loc == null) {
|
||||
continue;
|
||||
}
|
||||
GeoPoint geoPoint = MyTracksUtils.getGeoPoint(loc);
|
||||
Point pt = new Point();
|
||||
mapView.getProjection().toPixels(geoPoint, pt);
|
||||
canvas.save();
|
||||
canvas.translate(pt.x - (markerWidth / 2) + 3, pt.y - (markerHeight));
|
||||
if (wpt.getType() == Waypoint.TYPE_STATISTICS) {
|
||||
statsMarker.draw(canvas);
|
||||
} else {
|
||||
waypointMarker.draw(canvas);
|
||||
}
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
private void drawMyLocation(Canvas canvas, MapView mapView) {
|
||||
// Draw the arrow icon:
|
||||
if (myLocation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GeoPoint geoPoint = new GeoPoint(
|
||||
(int) (myLocation.getLatitude() * 1E6),
|
||||
(int) (myLocation.getLongitude() * 1E6));
|
||||
Point pt = new Point();
|
||||
mapView.getProjection().toPixels(geoPoint, pt);
|
||||
canvas.save();
|
||||
canvas.translate(pt.x - (arrowWidth / 2), pt.y - (arrowHeight / 2));
|
||||
arrow[lastHeading].draw(canvas);
|
||||
canvas.restore();
|
||||
|
||||
// Draw the error circle:
|
||||
float radius =
|
||||
mapView.getProjection().metersToEquatorPixels(myLocation.getAccuracy());
|
||||
canvas.drawCircle(pt.x, pt.y, radius, errorCirclePaint);
|
||||
}
|
||||
|
||||
private void drawTrack(Canvas canvas, MapView mapView) {
|
||||
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 viewRect = new Rect(cx - w, cy - h, cx + w, cy + h);
|
||||
|
||||
// Global bounding box, including points not visible
|
||||
int allMinLat, allMinLon, allMaxLat, allMaxLon;
|
||||
|
||||
synchronized (points) {
|
||||
int numPoints = points.size();
|
||||
if (numPoints < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
lastLocLon = locLon;
|
||||
lastLocLat = locLat;
|
||||
lastLocValid = locValid;
|
||||
lastLocVisible = locVisible;
|
||||
}
|
||||
}
|
||||
|
||||
canvas.drawPath(path, selectedTrackPaint);
|
||||
|
||||
if (drawBounds) {
|
||||
drawBoundingBox(canvas, mapView,
|
||||
allMinLat, allMinLon, allMaxLat, allMaxLon);
|
||||
}
|
||||
|
||||
// Draw the "End" marker:
|
||||
if (showEndMarker && lastValidLocation != null) {
|
||||
canvas.save();
|
||||
GeoPoint geoPoint = MyTracksUtils.getGeoPoint(lastValidLocation);
|
||||
mapView.getProjection().toPixels(geoPoint, pt);
|
||||
canvas.translate(pt.x - (markerWidth / 2), pt.y - markerHeight);
|
||||
endMarker.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
// Draw the "Start" marker:
|
||||
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) {
|
||||
draw(canvas, mapView, shadow);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pointer location (will be drawn on next invalidate).
|
||||
*/
|
||||
public void setMyLocation(Location myLocation) {
|
||||
this.myLocation = myLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pointer heading in degrees (will be drawn on next invalidate).
|
||||
*
|
||||
* @return true if the visible heading changed (i.e. a redraw of pointer is
|
||||
* potentially necessary)
|
||||
*/
|
||||
public boolean setHeading(float heading) {
|
||||
int newhdg = Math.round(-heading / 360 * 18 + 180);
|
||||
while (newhdg < 0)
|
||||
newhdg += 18;
|
||||
while (newhdg > 17)
|
||||
newhdg -= 18;
|
||||
if (newhdg != lastHeading) {
|
||||
lastHeading = newhdg;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTap(GeoPoint p, MapView mapView) {
|
||||
if (p.equals(mapView.getMapCenter())) {
|
||||
// There is (unfortunately) no good way to determine whether the tap was
|
||||
// caused by an actual tap on the screen or the track ball. If the
|
||||
// location is equal to the map center,then it was a track ball press with
|
||||
// very high likelihood.
|
||||
return false;
|
||||
}
|
||||
|
||||
final Location tapLocation = MyTracksUtils.getLocation(p);
|
||||
double dmin = Double.MAX_VALUE;
|
||||
Waypoint waypoint = null;
|
||||
for (int i = 0; i < waypoints.size(); i++) {
|
||||
final Location waypointLocation = waypoints.get(i).getLocation();
|
||||
if (waypointLocation == null) {
|
||||
continue;
|
||||
}
|
||||
final double d = waypointLocation.distanceTo(tapLocation);
|
||||
if (d < dmin) {
|
||||
dmin = d;
|
||||
waypoint = waypoints.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (waypoint != null
|
||||
&& dmin < 15000000 / Math.pow(2, mapView.getZoomLevel())) {
|
||||
Intent intent = new Intent(context, MyTracksWaypointDetails.class);
|
||||
intent.putExtra("waypointid", waypoint.getId());
|
||||
context.startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
return super.onTap(p, mapView);
|
||||
}
|
||||
|
||||
public void setDrawBounds(boolean drawBounds) {
|
||||
this.drawBounds = drawBounds;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user