From 2668df4f3293ccd7e347727e2345ab0b248881b4 Mon Sep 17 00:00:00 2001 From: sonora Date: Sat, 26 Nov 2016 20:31:26 +0100 Subject: [PATCH] GPX Analysis: Add elevation graph (for now only in meters) --- OsmAnd/res/layout/gpx_item_list_item.xml | 27 +++-- OsmAnd/src/net/osmand/plus/GPXUtilities.java | 18 ++++ .../osmand/plus/myplaces/ElevationView.java | 98 +++++++++++++++++++ .../plus/myplaces/SelectedGPXFragment.java | 14 +++ 4 files changed, 148 insertions(+), 9 deletions(-) create mode 100644 OsmAnd/src/net/osmand/plus/myplaces/ElevationView.java diff --git a/OsmAnd/res/layout/gpx_item_list_item.xml b/OsmAnd/res/layout/gpx_item_list_item.xml index c5b919e207..3591e419f6 100644 --- a/OsmAnd/res/layout/gpx_item_list_item.xml +++ b/OsmAnd/res/layout/gpx_item_list_item.xml @@ -48,14 +48,23 @@ tools:text="@string/lorem_ipsum" /> - + android:orientation="vertical"> + - \ No newline at end of file + + + diff --git a/OsmAnd/src/net/osmand/plus/GPXUtilities.java b/OsmAnd/src/net/osmand/plus/GPXUtilities.java index e0e2256a17..c9d4dd6ea6 100644 --- a/OsmAnd/src/net/osmand/plus/GPXUtilities.java +++ b/OsmAnd/src/net/osmand/plus/GPXUtilities.java @@ -91,6 +91,10 @@ public class GPXUtilities { } + public static class Elevation { + public double distance, elevation; + } + public static class WptPt extends GPXExtensions implements LocationPoint { public double lat; public double lon; @@ -286,6 +290,8 @@ public class GPXUtilities { return maxElevation != -100; } + public List elevationData; + public boolean isSpeedSpecified() { return avgSpeed > 0; } @@ -311,6 +317,8 @@ public class GPXUtilities { double channelBottom; boolean climb = false; + elevationData = new ArrayList<>(); + for (SplitSegment s : splitSegments) { final int numberOfPoints = s.getNumberOfPoints(); @@ -337,11 +345,16 @@ public class GPXUtilities { } double elevation = point.ele; + Elevation elevation1 = new Elevation(); if (!Double.isNaN(elevation)) { totalElevation += elevation; elevationPoints++; minElevation = Math.min(elevation, minElevation); maxElevation = Math.max(elevation, maxElevation); + + elevation1.elevation = elevation; + } else { + elevation1.elevation = 0; } float speed = (float) point.speed; @@ -433,7 +446,12 @@ public class GPXUtilities { // timeMoving0 = timeMoving0 + (point.time - prev.time); // totalDistanceMoving0 += calculations[0]; // } + } + + elevation1.distance = (j > 0) ? calculations[0] : 0; + elevationData.add(elevation1); + } } if (!isTimeSpecified()) { diff --git a/OsmAnd/src/net/osmand/plus/myplaces/ElevationView.java b/OsmAnd/src/net/osmand/plus/myplaces/ElevationView.java new file mode 100644 index 0000000000..99f82956a7 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/myplaces/ElevationView.java @@ -0,0 +1,98 @@ +package net.osmand.plus.myplaces; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Typeface; +import android.util.AttributeSet; +import android.util.Log; +import android.widget.ImageView; + +import net.osmand.plus.GPXUtilities; +import net.osmand.plus.R; + +import java.util.List; + +/** + * Created by maw on 02.09.15., original credit goes to gibbsnich + * Developed further by Hardy 2016-11 + */ +public class ElevationView extends ImageView { + + double maxElevation, minElevation; + float xDistance; + List elevationList; + + public ElevationView(Context ctx, AttributeSet as) { + super(ctx, as); + } + + public void onDraw(Canvas canvas) { + final float screenScale = getResources().getDisplayMetrics().density; + //TODO: Hardy: Perhaps support the other units of length in graph + int maxBase = (int)maxElevation / 100, minBase = (int)minElevation / 100; + maxBase += 1; + float yDistance = (maxBase-minBase) * 100; + + float xPer = (float)canvas.getWidth() / xDistance; + float yPer = (float)canvas.getHeight() / yDistance; + + Paint barPaint = new Paint(); + barPaint.setColor(getResources().getColor(R.color.dialog_inactive_text_color_dark)); + barPaint.setTextSize((int)(16f * screenScale + 0.5f)); + barPaint.setTypeface(Typeface.create(Typeface.DEFAULT_BOLD, Typeface.BOLD)); + float yTextLast = 9999f; + for (int i = minBase; i <= maxBase ; i++) { + float y = (float)canvas.getHeight() - yPer * (float)(i*100-(minBase*100)); + canvas.drawLine(0, y, canvas.getWidth(), y, barPaint); + if (yTextLast - y >= (int)(32f * screenScale + 0.5f)) { // Overlap prevention + canvas.drawText(String.valueOf(i*100) + " m", (int)(8f * screenScale + 0.5f), y-(int)(2f * screenScale + 0.5f), barPaint); + yTextLast = (float)(y); + } + } + canvas.drawLine(0, getResources().getDisplayMetrics().density, canvas.getWidth(), getResources().getDisplayMetrics().density, barPaint); + + float lastX = 0, lastY = 0; + float xDistSum = 0; + + Paint paint = new Paint(); + paint.setColor(getResources().getColor(R.color.gpx_altitude_asc)); + paint.setStrokeWidth((int)(2f * screenScale + 0.5f)); + boolean first = true; + if (elevationList != null) { + for (GPXUtilities.Elevation elevation : elevationList) { + xDistSum += elevation.distance; + float nextX = xPer * xDistSum; + float nextY = (float)canvas.getHeight() - yPer * (float)(elevation.elevation - (minBase*100f)); + if (first) { + first = false; + } else { + //Log.d("ElevationView", "curElevation: "+elevation.elevation+", drawLine: ("+lastX+", "+lastY+") -> ("+nextX+", "+nextY+")"); + canvas.drawLine(lastX, lastY, nextX, nextY, paint); + } + lastX = nextX; + lastY = nextY; + } + //Log.d("ElevationView", "yMin: "+yMin+", yMax = "+yMax+", smallestY = "+smallestYFound+", biggestY = "+biggestYFound); + } + } + + public void setElevationData(List elevationData) { + elevationList = elevationData; + } + + public void setMaxElevation(double max) { + maxElevation = max; + } + + public void setMinElevation(double min) { + minElevation = min; + } + + public void setTotalDistance(float dist) { + xDistance = dist; + } + +} + diff --git a/OsmAnd/src/net/osmand/plus/myplaces/SelectedGPXFragment.java b/OsmAnd/src/net/osmand/plus/myplaces/SelectedGPXFragment.java index 5c5a94ec88..01a007e536 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/SelectedGPXFragment.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/SelectedGPXFragment.java @@ -2,8 +2,11 @@ package net.osmand.plus.myplaces; import android.app.Activity; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.Paint; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; @@ -31,6 +34,7 @@ import net.osmand.data.FavouritePoint; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.plus.FavouritesDbHelper; +import net.osmand.plus.GPXUtilities; import net.osmand.plus.GPXUtilities.GPXFile; import net.osmand.plus.GPXUtilities.WptPt; import net.osmand.plus.GpxSelectionHelper; @@ -491,6 +495,16 @@ public class SelectedGPXFragment extends OsmAndListFragment { description.setVisibility(View.GONE); } + ElevationView elevationImg = (ElevationView) row.findViewById(R.id.elevation); + if (child.analysis != null && child.analysis.elevationData != null && child.analysis.isElevationSpecified() && (child.analysis.points > 1)) { + elevationImg.setElevationData(child.analysis.elevationData); + elevationImg.setMaxElevation(child.analysis.maxElevation); + elevationImg.setMinElevation(child.analysis.minElevation); + elevationImg.setTotalDistance(child.analysis.totalDistance); //Use raw data for graph, not channel detection noise filter (facilitates visual double check) + } else { + elevationImg.setVisibility(View.GONE); + } + return row; }