/** AirCasting - Share your Air! Copyright (C) 2011-2012 HabitatMap, Inc. 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 3 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, see <http://www.gnu.org/licenses/>. You can contact the authors by email at <info@habitatmap.org> */ package pl.llp.aircasting.view.overlay; import pl.llp.aircasting.activity.events.SessionLoadedEvent; import pl.llp.aircasting.activity.events.SessionStartedEvent; import pl.llp.aircasting.activity.events.SessionStoppedEvent; import pl.llp.aircasting.helper.ResourceHelper; import pl.llp.aircasting.model.SessionManager; import pl.llp.aircasting.util.map.PathSmoother; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; 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; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; import java.util.List; import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Iterables.get; import static com.google.common.collect.Iterables.isEmpty; import static com.google.common.collect.Iterables.skip; import static com.google.common.collect.Lists.newArrayList; public class RouteOverlay extends Overlay { public static final int OPAQUE = 255; public static final int SMOOTHING_BATCH = 10; @Inject PathSmoother pathSmoother; @Inject ResourceHelper resourceHelper; @Inject SessionManager sessionManager; private List<GeoPoint> points = newArrayList(); private List<GeoPoint> pendingPoints = newArrayList(); private List<GeoPoint> smoothedPoints = newArrayList(); private int zoomLevel; private GeoPoint mapCenter; private Paint paint; private Path path; private boolean skipDrawing; @Inject public void init() { preparePaint(); } public void addPoint(GeoPoint geoPoint) { pendingPoints.add(geoPoint); invalidate(); } public void invalidate() { path = null; } @Override public void draw(Canvas canvas, MapView view, boolean shadow) { if (shadow) return; if(skipDrawing()) { return; } if (isRefreshRequired(view)) { path = new Path(); preparePoints(); preparePath(view.getProjection()); zoomLevel = view.getZoomLevel(); mapCenter = view.getMapCenter(); } canvas.drawPath(path, paint); } @Subscribe public void onEvent(SessionStartedEvent event) { skipDrawing = false; } @Subscribe public void onEvent(SessionStoppedEvent event) { skipDrawing = true; } @Subscribe public void onEvent(SessionLoadedEvent event) { this.skipDrawing = false; } private boolean skipDrawing() { return this.skipDrawing || sessionManager.isLocationless(); } private boolean isRefreshRequired(MapView view) { return path == null || zoomLevel != view.getZoomLevel() || !mapCenter.equals(view.getMapCenter()); } private void preparePoints() { if (pendingPoints.size() > SMOOTHING_BATCH) { points.addAll(pendingPoints); pendingPoints.clear(); smoothedPoints = pathSmoother.getSmoothed(points); } } private void preparePaint() { paint = new Paint(); paint.setColor(resourceHelper.getGpsRoute()); paint.setAlpha(OPAQUE); paint.setStrokeWidth(3); paint.setStyle(Paint.Style.STROKE); paint.setStrokeJoin(Paint.Join.ROUND); paint.setStrokeCap(Paint.Cap.ROUND); paint.setAntiAlias(true); } private void preparePath(Projection projection) { Iterable<GeoPoint> pointsToDraw = concat(smoothedPoints, pendingPoints); if (isEmpty(pointsToDraw)) return; Point point = new Point(); projection.toPixels(get(pointsToDraw, 0), point); path.moveTo(point.x, point.y); for (GeoPoint geoPoint : skip(pointsToDraw, 1)) { projection.toPixels(geoPoint, point); path.lineTo(point.x, point.y); } } public void clear() { points.clear(); pendingPoints.clear(); smoothedPoints.clear(); } }