/** * Copyright (C) 2013 - 2015 the enviroCar community * <p> * This file is part of the enviroCar app. * <p> * The enviroCar app 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. * <p> * The enviroCar app 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. * <p> * You should have received a copy of the GNU General Public License along * with the enviroCar app. If not, see http://www.gnu.org/licenses/. */ package org.envirocar.app.view.trackdetails; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PointF; import android.graphics.Rect; import com.mapbox.mapboxsdk.geometry.BoundingBox; import com.mapbox.mapboxsdk.overlay.PathOverlay; import org.envirocar.core.entity.Measurement; import org.envirocar.core.entity.Track; import org.envirocar.core.logging.Logger; import java.util.ArrayList; import java.util.List; /** * @author dewall */ public class TrackSpeedMapOverlay extends PathOverlay { private static final Logger LOG = Logger.getLogger(TrackSpeedMapOverlay.class); private final Track mTrack; /** * Paint settings. */ private final Path mPath = new Path(); private Paint mLinePaint; // bounding rectangle for the current line segment. private final Rect mLineBounds = new Rect(); private BoundingBox mTrackBoundingBox; private BoundingBox mViewBoundingBox; private BoundingBox mScrollableLimitBox; private int mPointsPrecomputed; private ArrayList<PointF> mPoints; private ArrayList<Double> mValues; private boolean mOptimizePath = true; private final PointF mTempPoint1 = new PointF(); private final PointF mTempPoint2 = new PointF(); private List<Path> mPaths = new ArrayList<>(); private List<Paint> mPaints = new ArrayList<>(); /** * Constructor. * * @param track the track to create a overlay for. */ public TrackSpeedMapOverlay(Track track) { super(); mTrack = track; // Configure the line representation. Paint linePaint = new Paint(); linePaint.setStyle(Paint.Style.STROKE); linePaint.setColor(Color.BLUE); linePaint.setStrokeWidth(5); initPath(); setOverlayIndex(1); } // @Override // public void addPoint(double aLatitude, double aLongitude) { // mPoints.add(new PointF((float) aLatitude, (float) aLongitude)); // } // // @Override // public int getNumberOfPoints() { // return mPoints.size(); // } // // @Override // protected void draw(Canvas canvas, MapView mapView, boolean shadow) { // final int size = this.mPoints.size(); // // // nothing to paint // if (shadow || size < 2) { // return; // } // // final Projection pj = mapView.getProjection(); // // // precompute new points to the intermediate projection. // for (; this.mPointsPrecomputed < size; this.mPointsPrecomputed++) { // final PointF pt = this.mPoints.get(this.mPointsPrecomputed); // pj.toMapPixelsProjected((double) pt.x, (double) pt.y, pt); // // Paint paint = new Paint(); // paint.setColor(getColor(mValues.get(this.mPointsPrecomputed))); // paint.setStyle(Paint.Style.STROKE); // paint.setStrokeWidth(5); // mPaints.add(paint); // } // // PointF screenPoint0 = null; // points on screen // PointF screenPoint1; // PointF projectedPoint0; // points from the points list // PointF projectedPoint1; // // // clipping rectangle in the intermediate projection, to avoid performing projection. // final Rect clipBounds = pj.fromPixelsToProjected(pj.getScreenRect()); // // mPath.rewind(); // boolean needsDrawing = !mOptimizePath; // projectedPoint0 = this.mPoints.get(size - 1); // mLineBounds.set((int) projectedPoint0.x, (int) projectedPoint0.y, // (int) projectedPoint0.x, (int) projectedPoint0.y); // // mPaths.clear(); // // for (int i = size - 2; i >= 0; i--) { // // compute next points // projectedPoint1 = this.mPoints.get(i); // // //mLineBounds needs to be computed // mLineBounds.union((int) projectedPoint1.x, (int) projectedPoint1.y); // // // the starting point may be not calculated, because previous segment was out // // of clip bounds // if (screenPoint0 == null) { // screenPoint0 = pj.toMapPixelsTranslated(projectedPoint0, this.mTempPoint1); // } // // screenPoint1 = pj.toMapPixelsTranslated(projectedPoint1, this.mTempPoint2); // // // skip this point, too close to previous point // if (Math.abs(screenPoint1.x - screenPoint0.x) + // Math.abs(screenPoint1.y - screenPoint0.y) <= 1) { // continue; // } // // Path segment = new Path(); // segment.moveTo(screenPoint0.x, screenPoint0.y); // segment.lineTo(screenPoint1.x, screenPoint1.y); // mPaths.add(segment); // // // update starting point to next position // projectedPoint0 = projectedPoint1; // screenPoint0.x = screenPoint1.x; // screenPoint0.y = screenPoint1.y; // // if (mOptimizePath) { // needsDrawing = true; // mLineBounds.set((int) projectedPoint0.x, (int) projectedPoint0.y, // (int) projectedPoint0.x, (int) projectedPoint0.y); // } // } // // if (needsDrawing) { // for (int i = 0, s = mPaths.size(); i < s; i++) { // Path path = mPaths.get(i); // Paint p = mPaints.get(i); // float w = p.getStrokeWidth(); // p.setStrokeWidth(w / mapView.getScale()); // canvas.drawPath(path, p); // p.setStrokeWidth(w); // } // } // } // // private int getColor(Double value) { // if (value == null) { // return Color.BLACK; // } // ArgbEvaluator ev = new ArgbEvaluator(); // return (int) ev.evaluate((float) (value/80f), Color.RED, Color.GREEN); //// if (value < 50.0) { //// return Color.GREEN; //// } //// return Color.RED; // } /** * Initializes the track path and the bounding boxes required by the mapviews. */ private void initPath() { mPoints = new ArrayList<>(); mValues = new ArrayList<>(); List<Measurement> measurementList = mTrack.getMeasurements(); double maxLatitude = Double.MIN_VALUE; double minLatitude = Double.MAX_VALUE; double maxLongitude = Double.MIN_VALUE; double minLongitude = Double.MAX_VALUE; // For each measurement value add the longitude and latitude coordinates as a new // mappoint to the overlay network. In addition, try to find out the maximum and minimum // lon/lat coordinates for the zoom value of the mapview. for (Measurement measurement : measurementList) { double latitude = measurement.getLatitude(); double longitude = measurement.getLongitude(); if(latitude == 0.0 || longitude == 0.0) { LOG.warn("An coordinate was 0.0"); continue; } addPoint(measurement.getLatitude(), measurement.getLongitude()); mValues.add(measurement.getProperty(Measurement.PropertyKey.SPEED)); maxLatitude = Math.max(maxLatitude, latitude); minLatitude = Math.min(minLatitude, latitude); maxLongitude = Math.max(maxLongitude, longitude); minLongitude = Math.min(minLongitude, longitude); } LOG.warn("maxLongitude = " + maxLongitude); // The bounding box of the pathoverlay. mTrackBoundingBox = new BoundingBox(maxLatitude, maxLongitude, minLatitude, minLongitude); // The view bounding box of the pathoverlay mViewBoundingBox = new BoundingBox( mTrackBoundingBox.getLatNorth() + 0.01, mTrackBoundingBox.getLonEast() + 0.01, mTrackBoundingBox.getLatSouth() - 0.01, mTrackBoundingBox.getLonWest() - 0.01); // The bounding box that limits the scrolling of the mapview. mScrollableLimitBox = new BoundingBox( mTrackBoundingBox.getLatNorth() + 0.05, mTrackBoundingBox.getLonEast() + 0.05, mTrackBoundingBox.getLatSouth() - 0.05, mTrackBoundingBox.getLonWest() - 0.05); } /** * Gets the {@link BoundingBox} of the track. * * @return the BoundingBox of the track. */ public BoundingBox getTrackBoundingBox() { return mTrackBoundingBox; } /** * Gets the view {@link BoundingBox} of the track, which is a slightly buffered bounding box * for zoom purposes of the track. * * @return the BoundingBox of the track. */ public BoundingBox getViewBoundingBox() { return mViewBoundingBox; } /** * Gets the {@link BoundingBox} that is used as a scrollable limit of the track in the mapview. * * @return the BoundingBox of the track. */ public BoundingBox getScrollableLimitBox() { return mScrollableLimitBox; } }