package edu.mit.mobile.android.maps;
/*
* Copyright (C) 2011 MIT Mobile Experience Lab
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import java.util.LinkedList;
import java.util.List;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.Point;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
/**
* Shows a single path, with an optional outline.
*
* @author steve
*
*/
public class PathOverlay extends Overlay {
@SuppressWarnings("unused")
private static final String TAG = PathOverlay.class.getSimpleName();
private final Paint mPathPaint;
private final Paint mPathPaintOutline;
private PathEffect mPathEffect;
private List<GeoPoint> mPathGeo = new LinkedList<GeoPoint>();
private Path mPath;
private final boolean animate = false;
private boolean mShowOutline = true;
private boolean mBoundsDirty = true;
public PathOverlay(Context context){
this(context, null);
}
public PathOverlay(Context context, Paint pathPaint) {
if (pathPaint != null){
mPathPaint = pathPaint;
}else{
// TODO bring this to XML somehow
mPathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPathPaint.setColor(Color.rgb(0x2c, 0xb2, 0xb2));
mPathPaint.setStyle(Paint.Style.STROKE);
mPathEffect = new DashPathEffect (new float[]{15, 8}, 0);
mPathPaint.setPathEffect(mPathEffect);
mPathPaint.setStrokeWidth(5);
mPathPaint.setStrokeJoin(Paint.Join.ROUND);
mPathPaint.setStrokeCap(Paint.Cap.ROUND);
}
mPathPaintOutline = new Paint(Paint.ANTI_ALIAS_FLAG);
mPathPaintOutline.setColor(Color.argb(192, 255, 255, 255));
mPathPaintOutline.setStyle(Paint.Style.STROKE);
mPathPaintOutline.setStrokeWidth(mPathPaint.getStrokeWidth() * 2);
mPathPaintOutline.setStrokeJoin(Paint.Join.ROUND);
mPathPaintOutline.setStrokeCap(Paint.Cap.ROUND);
}
/**
* Add a point to the path.
*
* @param latitudeE6
* @param longitudeE6
*/
public void addPoint(int latitudeE6, int longitudeE6){
mPathGeo.add(new GeoPoint(latitudeE6, longitudeE6));
mBoundsDirty = true;
}
/**
* Add a point to the path.
*
* @param point
*/
public void addPoint(GeoPoint point){
mPathGeo.add(point);
mBoundsDirty = true;
}
/**
* Clear the path.
*/
public void clearPath(){
mPathGeo.clear();
mBoundsDirty = true;
}
public void setPath(List<GeoPoint> path){
mPathGeo = path;
mBoundsDirty = true;
}
/**
* Adds a white/translucent outline around your path to improve visibility.
*
* @param show show the outline
*/
public void setShowOutline(boolean show){
mShowOutline = show;
}
private int maxLat, minLat;
private int maxLon, minLon;
private void updatePath(Projection projection){
mPath = new Path();
final Point p = new Point();
boolean first = true;
for (final GeoPoint gp : mPathGeo){
projection.toPixels(gp, p);
if (first){
mPath.moveTo(p.x, p.y);
first = false;
}else{
mPath.lineTo(p.x, p.y);
}
}
}
@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
if (!shadow){
if (animate){
mPathEffect = new DashPathEffect (new float[]{20, 20}, ((System.currentTimeMillis() / 10) % 400)/10.0f);
mPathPaint.setPathEffect(mPathEffect);
}
updatePath(mapView.getProjection());
if (mShowOutline){
canvas.drawPath(mPath, mPathPaintOutline);
}
canvas.drawPath(mPath, mPathPaint);
if (animate) {
mapView.postInvalidateDelayed(50);
}
}
};
private void computeBounds(){
if (!mBoundsDirty){
return;
}
maxLat = minLat = mPathGeo.get(0).getLatitudeE6();
maxLon = minLon = mPathGeo.get(0).getLongitudeE6();
int lat, lon;
for (final GeoPoint gp : mPathGeo){
lat = gp.getLatitudeE6();
lon = gp.getLongitudeE6();
maxLat = Math.max(maxLat, lat);
minLat = Math.min(minLat, lat);
maxLon = Math.max(maxLon, lon);
minLon = Math.min(minLon, lon);
}
mBoundsDirty = false;
}
public int getLatSpanE6(){
if (mPathGeo.size() == 0){
return 0;
}
computeBounds();
return Math.abs(maxLat - minLat);
}
public int getLonSpanE6(){
if (mPathGeo.size() == 0){
return 0;
}
computeBounds();
return Math.abs(maxLon - minLon);
}
/**
* this does not work properly when crossing -180/180 boundaries.
*
* @return the center point of the path according to its bounds or null if there are no points on the path.
*/
public GeoPoint getCenter() {
if (mPathGeo.size() == 0){
return null;
}
computeBounds();
return new GeoPoint((maxLat - minLat)/2 + minLat, (maxLon - minLon)/2 + minLon);
}
}