/**
*
*/
package com.geeksville.maps;
import java.util.ArrayList;
import org.andnav.osm.util.GeoPoint;
import org.andnav.osm.views.OpenStreetMapView;
import org.andnav.osm.views.OpenStreetMapView.OpenStreetMapViewProjection;
import org.andnav.osm.views.overlay.OpenStreetMapViewPathOverlay;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.PaintDrawable;
import android.util.Log;
import com.geeksville.location.LocationList;
/**
* Overlay a tracklog plot over a map
*
* @author kevinh
*
*/
public class TracklogOverlay extends OpenStreetMapViewPathOverlay {
LocationList tracklog;
public final int DETAIL_THRESHOLD = 1000; //the last n points that are drawn with full color detail
/**
* Max up/down millimeter/sec -- FIXME: set according to vario limits
*/
float maxMMeterSec = 5.0f,
minMMeterSec = -5.0f;
/**
* The colors we use for drawing our tracklog
*/
Paint[] trackPaints = new Paint[256];
private ArrayList<Point> mPoints;
private ArrayList<Paint> mColors;
static final int POINT_BUFFER_SIZE = 256; //should be a multiple of 4
private float[] mPointBuffer = new float[POINT_BUFFER_SIZE];
private final Point mTempPoint1 = new Point();
private final Point mTempPoint2 = new Point();
public TracklogOverlay(Context ctx, LocationList locs) {
super(Color.RED, ctx);
tracklog = locs;
precalcTrackPaints();
}
private void precalcTrackPaints() {
for (int i=0;i<256;i++) {
Paint trackPaint = new Paint();
trackPaint.setColor(Color.rgb(255-Math.abs(127-i)*2, i, 255-i)); // blue -> red -> green
trackPaint.setStyle(Style.STROKE);
trackPaint.setStrokeWidth(2);
trackPaints[i]=trackPaint;
}
}
@Override
public void clearPath()
{
this.mPoints = new ArrayList<Point>();
this.mColors = new ArrayList<Paint>();
}
/**
* This method draws the line.
* Note - highly optimized to handle long paths, proceed with care. Should be fine up to 10K points.
*/
@Override
protected void onDraw(Canvas canvas, OpenStreetMapView mapView)
{
if (this.tracklog.numPoints() < 2)
{
//nothing to paint
return;
}
final OpenStreetMapViewProjection pj = mapView.getProjection();
int size = this.tracklog.numPoints();
if (size > mPoints.size()) {
for (int i=mPoints.size(); i<size; i++) {
GeoPoint gpt = this.tracklog.getGeoPoint(i);
Point pt = new Point(gpt.getLatitudeE6(), gpt.getLongitudeE6());
pj.toMapPixelsProjected(pt.x, pt.y, pt); //convert to map projection
mPoints.add(pt);
if (i>0) {
float rise = (tracklog.getAltitudeMM(i) - tracklog.getAltitudeMM(i-1)) / (tracklog.getTimeMsec(i) - tracklog.getTimeMsec(i-1)); // mm/ms = m/s
rise = Math.min(Math.max(rise,minMMeterSec),maxMMeterSec); // bound to limits
float level = (rise - minMMeterSec) / (maxMMeterSec - minMMeterSec) * 255.f;
mColors.add(trackPaints[(int)level]);
} else
mColors.add(trackPaints[127]);
}
}
Point screenPoint0 = null; //points on screen
Point screenPoint1 = null;
Point projectedPoint0; //points from the points list
Point projectedPoint1;
float [] buffer = this.mPointBuffer;
int bufferCount = 0;
Rect clipBounds = pj.fromPixelsToProjected(canvas.getClipBounds()); // clipping rectangle in the intermediate projection, to avoid performing projection.
Rect lineBounds = new Rect(); // bounding rectangle for the current line segment.
projectedPoint0 = this.mPoints.get(size - 1);
lineBounds.set(projectedPoint0.x, projectedPoint0.y, projectedPoint0.x, projectedPoint0.y);
for(int i = size - 2; i >= 0; i--)
{
//compute next points
projectedPoint1 = this.mPoints.get(i);
lineBounds.union(projectedPoint1.x, projectedPoint1.y);
if (!Rect.intersects(clipBounds, lineBounds))
{
//skip this line, move to next point
projectedPoint0 = projectedPoint1;
screenPoint0 = null;
continue;
}
// 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;
}
//add new line to buffer
buffer[bufferCount] = screenPoint0.x;
buffer[bufferCount + 1] = screenPoint0.y;
buffer[bufferCount + 2] = screenPoint1.x;
buffer[bufferCount + 3] = screenPoint1.y;
bufferCount += 4;
if (i > size - DETAIL_THRESHOLD) { // if we are in the detailed range
canvas.drawLines(buffer, this.mColors.get(i)); // paint with accurate colors
bufferCount = 0;
} else
if (bufferCount == POINT_BUFFER_SIZE) { // else just paint in red
canvas.drawLines(buffer, this.trackPaints[127]);
bufferCount = 0;
}
//update starting point to next position
projectedPoint0 = projectedPoint1;
screenPoint0.x = screenPoint1.x;
screenPoint0.y = screenPoint1.y;
lineBounds.set(projectedPoint0.x, projectedPoint0.y, projectedPoint0.x, projectedPoint0.y);
}
if (bufferCount >0 )
{
canvas.drawLines(buffer, 0, bufferCount, this.mPaint);
}
}
}