forked from upstream-mirrors/OpenTracks
Refactor ChartView/ChartFragment to use ChartPoint.
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package de.dennisguse.opentracks.chart;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
@@ -44,7 +45,17 @@ public class ChartValueSeriesTest {
|
||||
R.string.description_elevation_metric,
|
||||
R.string.description_elevation_imperial,
|
||||
R.color.chart_elevation_fill,
|
||||
R.color.chart_elevation_border);
|
||||
R.color.chart_elevation_border) {
|
||||
@Override
|
||||
double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||
return chartPoint.getElevation();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawIfChartPointHasNoData() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -63,8 +74,8 @@ public class ChartValueSeriesTest {
|
||||
|
||||
@Test
|
||||
public void testSmallUpdates() {
|
||||
series.update(0);
|
||||
series.update(10);
|
||||
series.update(new ChartPoint(0));
|
||||
series.update(new ChartPoint(10));
|
||||
series.updateDimension();
|
||||
Assert.assertEquals(100, series.getInterval());
|
||||
Assert.assertEquals(0, series.getMinMarkerValue());
|
||||
@@ -73,8 +84,8 @@ public class ChartValueSeriesTest {
|
||||
|
||||
@Test
|
||||
public void testBigUpdates() {
|
||||
series.update(0);
|
||||
series.update(901);
|
||||
series.update(new ChartPoint(0));
|
||||
series.update(new ChartPoint(901));
|
||||
series.updateDimension();
|
||||
Assert.assertEquals(1000, series.getInterval());
|
||||
Assert.assertEquals(0, series.getMinMarkerValue());
|
||||
@@ -83,8 +94,8 @@ public class ChartValueSeriesTest {
|
||||
|
||||
@Test
|
||||
public void testNotZeroBasedUpdates() {
|
||||
series.update(220);
|
||||
series.update(250);
|
||||
series.update(new ChartPoint(220));
|
||||
series.update(new ChartPoint(250));
|
||||
series.updateDimension();
|
||||
Assert.assertEquals(100, series.getInterval());
|
||||
Assert.assertEquals(200, series.getMinMarkerValue());
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.TrackStubUtils;
|
||||
import de.dennisguse.opentracks.chart.ChartPoint;
|
||||
import de.dennisguse.opentracks.chart.ChartView;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
@@ -74,10 +75,10 @@ public class ChartFragmentTest {
|
||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
|
||||
|
||||
// No input.
|
||||
double[] point = chartFragment.createPendingPoint(trackPoint);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.CADENCE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.POWER_SERIES + 1], 0.01);
|
||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
|
||||
Assert.assertEquals(Float.NaN, point.getHeartRate(), 0.01);
|
||||
Assert.assertEquals(Float.NaN, point.getCadence(), 0.01);
|
||||
Assert.assertEquals(Float.NaN, point.getPower(), 0.01);
|
||||
|
||||
// Input incorrect state.
|
||||
// Creates SensorData.
|
||||
@@ -85,9 +86,9 @@ public class ChartFragmentTest {
|
||||
trackPoint.setSensorDataSet(sensorDataSet);
|
||||
// Test.
|
||||
point = chartFragment.createPendingPoint(trackPoint);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.CADENCE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.POWER_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(Float.NaN, point.getHeartRate(), 0.01);
|
||||
Assert.assertEquals(Float.NaN, point.getCadence(), 0.01);
|
||||
Assert.assertEquals(Float.NaN, point.getPower(), 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,10 +98,10 @@ public class ChartFragmentTest {
|
||||
public void testCreatePendingPoint_sensorCorrect() {
|
||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
|
||||
// No input.
|
||||
double[] point = chartFragment.createPendingPoint(trackPoint);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.CADENCE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.POWER_SERIES + 1], 0.01);
|
||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
|
||||
Assert.assertEquals(Float.NaN, point.getHeartRate(), 0.01);
|
||||
Assert.assertEquals(Float.NaN, point.getCadence(), 0.01);
|
||||
Assert.assertEquals(Float.NaN, point.getPower(), 0.01);
|
||||
|
||||
// Creates SensorData.
|
||||
SensorDataSet sensorDataSet = new SensorDataSet(100, 101, 102);
|
||||
@@ -109,9 +110,9 @@ public class ChartFragmentTest {
|
||||
trackPoint.setSensorDataSet(sensorDataSet);
|
||||
// Test.
|
||||
point = chartFragment.createPendingPoint(trackPoint);
|
||||
Assert.assertEquals(100.0, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(101.0, point[ChartView.CADENCE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(102.0, point[ChartView.POWER_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(100.0, point.getHeartRate(), 0.01);
|
||||
Assert.assertEquals(101.0, point.getCadence(), 0.01);
|
||||
Assert.assertEquals(102.0, point.getPower(), 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,13 +123,13 @@ public class ChartFragmentTest {
|
||||
chartFragment.setChartByDistance(true);
|
||||
// Resets last location and writes first location.
|
||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
||||
double[] point = chartFragment.createPendingPoint(trackPoint1);
|
||||
Assert.assertEquals(0.0, point[0], 0.01);
|
||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
||||
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01);
|
||||
|
||||
// The second is a same location, just different time.
|
||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
||||
point = chartFragment.createPendingPoint(trackPoint2);
|
||||
Assert.assertEquals(0.0, point[0], 0.01);
|
||||
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01);
|
||||
|
||||
// The third location is a new location, and use metric.
|
||||
TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint();
|
||||
@@ -140,7 +141,7 @@ public class ChartFragmentTest {
|
||||
Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(),
|
||||
trackPoint3.getLatitude(), trackPoint3.getLongitude(), results);
|
||||
double distance1 = results[0] * UnitConversions.M_TO_KM;
|
||||
Assert.assertEquals(distance1, point[0], 0.01);
|
||||
Assert.assertEquals(distance1, point.getTimeOrDistance(), 0.01);
|
||||
|
||||
// The fourth location is a new location, and use metric.
|
||||
TrackPoint trackPoint4 = TrackStubUtils.createDefaultTrackPoint();
|
||||
@@ -151,7 +152,7 @@ public class ChartFragmentTest {
|
||||
Location.distanceBetween(trackPoint3.getLatitude(), trackPoint3.getLongitude(),
|
||||
trackPoint4.getLatitude(), trackPoint4.getLongitude(), results);
|
||||
double distance2 = results[0] * UnitConversions.M_TO_KM;
|
||||
Assert.assertEquals((distance1 + distance2), point[0], 0.01);
|
||||
Assert.assertEquals((distance1 + distance2), point.getTimeOrDistance(), 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,8 +167,8 @@ public class ChartFragmentTest {
|
||||
|
||||
// The first is a same location, just different time.
|
||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
||||
double[] point = chartFragment.createPendingPoint(trackPoint1);
|
||||
Assert.assertEquals(0.0, point[0], 0.01);
|
||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
||||
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01);
|
||||
|
||||
// The second location is a new location, and use imperial.
|
||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
||||
@@ -181,7 +182,7 @@ public class ChartFragmentTest {
|
||||
float[] results = new float[4];
|
||||
Location.distanceBetween(trackPoint1.getLatitude(), trackPoint1.getLongitude(), trackPoint2.getLatitude(), trackPoint2.getLongitude(), results);
|
||||
double distance1 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
|
||||
Assert.assertEquals(distance1, point[0], 0.01);
|
||||
Assert.assertEquals(distance1, point.getTimeOrDistance(), 0.01);
|
||||
|
||||
// The third location is a new location, and use imperial.
|
||||
TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint();
|
||||
@@ -194,7 +195,7 @@ public class ChartFragmentTest {
|
||||
*/
|
||||
Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(), trackPoint3.getLatitude(), trackPoint3.getLongitude(), results);
|
||||
double distance2 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
|
||||
Assert.assertEquals(distance1 + distance2, point[0], 0.01);
|
||||
Assert.assertEquals(distance1 + distance2, point.getTimeOrDistance(), 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,13 +205,13 @@ public class ChartFragmentTest {
|
||||
public void testCreatePendingPoint_time() {
|
||||
chartFragment.setChartByDistance(false);
|
||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
||||
double[] point = chartFragment.createPendingPoint(trackPoint1);
|
||||
Assert.assertEquals(0.0, point[0], 0.01);
|
||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
||||
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01);
|
||||
long timeSpan = 222;
|
||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
||||
trackPoint2.setTime(trackPoint1.getTime() + timeSpan);
|
||||
point = chartFragment.createPendingPoint(trackPoint2);
|
||||
Assert.assertEquals((double) timeSpan, point[0], 0.01);
|
||||
Assert.assertEquals((double) timeSpan, point.getTimeOrDistance(), 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,8 +225,8 @@ public class ChartFragmentTest {
|
||||
* At first, clear old points of elevation, so give true to the second parameter.
|
||||
* Then only one value INITIAL_ALTITUDE in buffer.
|
||||
*/
|
||||
double[] point = chartFragment.createPendingPoint(trackPoint1);
|
||||
Assert.assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point[ChartView.ELEVATION_SERIES + 1], 0.01);
|
||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
||||
Assert.assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point.getElevation(), 0.01);
|
||||
|
||||
/*
|
||||
* Send another value to buffer, now there are two values, INITIAL_ALTITUDE and INITIAL_ALTITUDE * 2.
|
||||
@@ -233,7 +234,7 @@ public class ChartFragmentTest {
|
||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
||||
trackPoint2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
|
||||
point = chartFragment.createPendingPoint(trackPoint2);
|
||||
Assert.assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point[ChartView.ELEVATION_SERIES + 1], 0.01);
|
||||
Assert.assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point.getElevation(), 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,8 +250,8 @@ public class ChartFragmentTest {
|
||||
*/
|
||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
||||
trackPoint1.setSpeed(128.5f);
|
||||
double[] point = chartFragment.createPendingPoint(trackPoint1);
|
||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
|
||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
||||
Assert.assertEquals(0.0, point.getSpeed(), 0.01);
|
||||
|
||||
/*
|
||||
* Tests the logic when both metricUnits and reportSpeed are true.
|
||||
@@ -264,7 +265,7 @@ public class ChartFragmentTest {
|
||||
trackPoint2.setTime(trackPoint1.getTime() + 222);
|
||||
trackPoint2.setSpeed(130);
|
||||
point = chartFragment.createPendingPoint(trackPoint2);
|
||||
Assert.assertEquals(130.0 * UnitConversions.MS_TO_KMH, point[ChartView.SPEED_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(130.0 * UnitConversions.MS_TO_KMH, point.getSpeed(), 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,8 +278,8 @@ public class ChartFragmentTest {
|
||||
// First data point is not added to the speed buffer
|
||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
||||
trackPoint1.setSpeed(100.0f);
|
||||
double[] point = chartFragment.createPendingPoint(trackPoint1);
|
||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
|
||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
||||
Assert.assertEquals(0.0, point.getSpeed(), 0.01);
|
||||
|
||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
||||
|
||||
@@ -289,7 +290,7 @@ public class ChartFragmentTest {
|
||||
trackPoint2.setTime(trackPoint2.getTime() + 222);
|
||||
trackPoint2.setSpeed(102);
|
||||
point = chartFragment.createPendingPoint(trackPoint2);
|
||||
Assert.assertEquals(102.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI, point[ChartView.SPEED_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(102.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,8 +303,8 @@ public class ChartFragmentTest {
|
||||
// First data point is not added to the speed buffer
|
||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
||||
trackPoint1.setSpeed(100.0f);
|
||||
double[] point = chartFragment.createPendingPoint(trackPoint1);
|
||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
|
||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
||||
Assert.assertEquals(0.0, point.getSpeed(), 0.01);
|
||||
|
||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
||||
|
||||
@@ -314,7 +315,7 @@ public class ChartFragmentTest {
|
||||
trackPoint2.setTime(trackPoint2.getTime() + 222);
|
||||
trackPoint2.setSpeed(102);
|
||||
point = chartFragment.createPendingPoint(trackPoint2);
|
||||
Assert.assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MS_TO_KMH), point[ChartView.PACE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MS_TO_KMH), point.getPace(), 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -325,7 +326,7 @@ public class ChartFragmentTest {
|
||||
chartFragment.setReportSpeed(false);
|
||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
|
||||
trackPoint.setSpeed(0);
|
||||
double[] point = chartFragment.createPendingPoint(trackPoint);
|
||||
Assert.assertEquals(0.0, point[ChartView.PACE_SERIES + 1], 0.01);
|
||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
|
||||
Assert.assertEquals(0.0, point.getPace(), 0.01);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
if (recordingTrackId == -1L) {
|
||||
TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound();
|
||||
if (service == null) {
|
||||
Log.d(TAG, "it could not get service");
|
||||
Log.d(TAG, "could not get TrackRecordingService");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package de.dennisguse.opentracks.chart;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
|
||||
public class ChartPoint {
|
||||
//X-axis
|
||||
private double timeOrDistance;
|
||||
|
||||
//Y-axis
|
||||
private double elevation;
|
||||
private double speed;
|
||||
private double pace;
|
||||
private double heartRate = Double.NaN;
|
||||
private double cadence = Double.NaN;
|
||||
private double power = Double.NaN;
|
||||
|
||||
@VisibleForTesting
|
||||
ChartPoint(double elevation) {
|
||||
this.elevation = elevation;
|
||||
}
|
||||
|
||||
public ChartPoint(@NonNull TrackStatisticsUpdater trackStatisticsUpdater, SensorDataSet sensorDataSet, boolean chartByDistance, boolean metricUnits) {
|
||||
TrackStatistics trackStatistics = trackStatisticsUpdater.getTrackStatistics();
|
||||
|
||||
if (chartByDistance) {
|
||||
double distance = trackStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||
if (!metricUnits) {
|
||||
distance *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
timeOrDistance = distance;
|
||||
} else {
|
||||
timeOrDistance = trackStatistics.getTotalTime();
|
||||
}
|
||||
|
||||
elevation = trackStatisticsUpdater.getSmoothedElevation();
|
||||
if (!metricUnits) {
|
||||
elevation *= UnitConversions.M_TO_FT;
|
||||
}
|
||||
|
||||
speed = trackStatisticsUpdater.getSmoothedSpeed() * UnitConversions.MS_TO_KMH;
|
||||
if (!metricUnits) {
|
||||
speed *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
pace = speed == 0 ? 0.0 : 60.0 / speed;
|
||||
if (sensorDataSet != null) {
|
||||
if (sensorDataSet.hasHeartRate()) {
|
||||
heartRate = sensorDataSet.getHeartRate();
|
||||
}
|
||||
if (sensorDataSet.hasCadence()) {
|
||||
cadence = sensorDataSet.getCadence();
|
||||
}
|
||||
if (sensorDataSet.hasPower()) {
|
||||
power = sensorDataSet.getPower();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double getTimeOrDistance() {
|
||||
return timeOrDistance;
|
||||
}
|
||||
|
||||
public double getElevation() {
|
||||
return elevation;
|
||||
}
|
||||
|
||||
public double getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
public double getPace() {
|
||||
return pace;
|
||||
}
|
||||
|
||||
public boolean hasHeartRate() {
|
||||
return Double.isNaN(heartRate);
|
||||
}
|
||||
|
||||
public double getHeartRate() {
|
||||
return heartRate;
|
||||
}
|
||||
|
||||
public boolean hasCadence() {
|
||||
return Double.isNaN(cadence);
|
||||
}
|
||||
|
||||
public double getCadence() {
|
||||
return cadence;
|
||||
}
|
||||
|
||||
public boolean hasPower() {
|
||||
return Double.isNaN(power);
|
||||
}
|
||||
|
||||
public double getPower() {
|
||||
return power;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import android.graphics.Paint.Align;
|
||||
import android.graphics.Paint.Style;
|
||||
import android.graphics.Path;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
|
||||
import de.dennisguse.opentracks.stats.ExtremityMonitor;
|
||||
@@ -32,7 +34,7 @@ import de.dennisguse.opentracks.stats.ExtremityMonitor;
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
class ChartValueSeries {
|
||||
abstract class ChartValueSeries {
|
||||
|
||||
private static final float STROKE_WIDTH = 2f;
|
||||
|
||||
@@ -126,26 +128,26 @@ class ChartValueSeries {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the series with a new value.
|
||||
*
|
||||
* @param value the new value
|
||||
* Updates the series with a new {@link ChartPoint}.
|
||||
*/
|
||||
void update(double value) {
|
||||
extremityMonitor.update(value);
|
||||
void update(ChartPoint chartPoint) {
|
||||
if (isChartPointValid(chartPoint)) {
|
||||
extremityMonitor.update(extractDataFromChartPoint(chartPoint));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path.
|
||||
*/
|
||||
abstract double extractDataFromChartPoint(@NonNull ChartPoint chartPoint);
|
||||
|
||||
boolean isChartPointValid(@NonNull ChartPoint chartPoint) {
|
||||
return !Double.isNaN(extractDataFromChartPoint(chartPoint));
|
||||
}
|
||||
|
||||
protected abstract boolean drawIfChartPointHasNoData();
|
||||
|
||||
Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the path on canvas.
|
||||
*
|
||||
* @param canvas the canvas
|
||||
*/
|
||||
void drawPath(Canvas canvas) {
|
||||
canvas.drawPath(path, fillPaint);
|
||||
canvas.drawPath(path, strokePaint);
|
||||
|
||||
@@ -33,6 +33,7 @@ import android.view.ScaleGestureDetector;
|
||||
import android.view.View;
|
||||
import android.widget.Scroller;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.GestureDetectorCompat;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
@@ -62,14 +63,6 @@ public class ChartView extends View {
|
||||
|
||||
static final int Y_AXIS_INTERVALS = 5;
|
||||
|
||||
public static final int NUM_SERIES = 6;
|
||||
public static final int ELEVATION_SERIES = 0;
|
||||
public static final int SPEED_SERIES = 1;
|
||||
public static final int PACE_SERIES = 2;
|
||||
public static final int HEART_RATE_SERIES = 3;
|
||||
public static final int CADENCE_SERIES = 4;
|
||||
public static final int POWER_SERIES = 5;
|
||||
|
||||
private static final int TARGET_X_AXIS_INTERVALS = 4;
|
||||
|
||||
private static final int MIN_ZOOM_LEVEL = 1;
|
||||
@@ -89,8 +82,11 @@ public class ChartView extends View {
|
||||
X_FRACTION_FORMAT.setMinimumFractionDigits(1);
|
||||
}
|
||||
|
||||
private final ChartValueSeries[] series = new ChartValueSeries[NUM_SERIES];
|
||||
private final List<double[]> chartData = new ArrayList<>();
|
||||
private final List<ChartValueSeries> seriesList = new ArrayList<>();
|
||||
private final ChartValueSeries speedSeries;
|
||||
private final ChartValueSeries paceSeries;
|
||||
|
||||
private final List<ChartPoint> chartData = new ArrayList<>();
|
||||
private final List<Waypoint> waypoints = new ArrayList<>();
|
||||
private final ExtremityMonitor xExtremityMonitor = new ExtremityMonitor();
|
||||
private final Paint axisPaint;
|
||||
@@ -192,58 +188,124 @@ public class ChartView extends View {
|
||||
}
|
||||
});
|
||||
|
||||
public ChartView(Context context, boolean chartByDistance) {
|
||||
public ChartView(Context context, final boolean chartByDistance) {
|
||||
super(context);
|
||||
this.chartByDistance = chartByDistance;
|
||||
|
||||
series[ELEVATION_SERIES] = new ChartValueSeries(context,
|
||||
seriesList.add(new ChartValueSeries(context,
|
||||
Integer.MIN_VALUE,
|
||||
Integer.MAX_VALUE,
|
||||
new int[]{5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000},
|
||||
R.string.description_elevation_metric,
|
||||
R.string.description_elevation_imperial,
|
||||
R.color.chart_elevation_fill,
|
||||
R.color.chart_elevation_border);
|
||||
series[SPEED_SERIES] = new ChartValueSeries(context,
|
||||
R.color.chart_elevation_border) {
|
||||
@Override
|
||||
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||
return chartPoint.getElevation();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawIfChartPointHasNoData() {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
speedSeries = new ChartValueSeries(context,
|
||||
0,
|
||||
Integer.MAX_VALUE,
|
||||
new int[]{1, 5, 10, 20, 50, 100},
|
||||
R.string.description_speed_metric,
|
||||
R.string.description_speed_imperial,
|
||||
R.color.chart_speed_fill,
|
||||
R.color.chart_speed_border);
|
||||
series[PACE_SERIES] = new ChartValueSeries(context,
|
||||
R.color.chart_speed_border) {
|
||||
@Override
|
||||
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||
return chartPoint.getSpeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawIfChartPointHasNoData() {
|
||||
return reportSpeed;
|
||||
}
|
||||
};
|
||||
seriesList.add(speedSeries);
|
||||
|
||||
paceSeries = new ChartValueSeries(context,
|
||||
0,
|
||||
Integer.MAX_VALUE,
|
||||
new int[]{1, 2, 5, 10, 15, 20, 30, 60, 120},
|
||||
R.string.description_pace_metric,
|
||||
R.string.description_pace_imperial,
|
||||
R.color.chart_pace_fill,
|
||||
R.color.chart_pace_border);
|
||||
series[HEART_RATE_SERIES] = new ChartValueSeries(context,
|
||||
R.color.chart_pace_border) {
|
||||
@Override
|
||||
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||
return chartPoint.getPace();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawIfChartPointHasNoData() {
|
||||
return !reportSpeed;
|
||||
}
|
||||
};
|
||||
seriesList.add(paceSeries);
|
||||
|
||||
seriesList.add(new ChartValueSeries(context,
|
||||
0,
|
||||
Integer.MAX_VALUE,
|
||||
new int[]{25, 50},
|
||||
R.string.description_sensor_heart_rate,
|
||||
R.string.description_sensor_heart_rate,
|
||||
R.color.chart_heart_rate_fill,
|
||||
R.color.chart_heart_rate_border);
|
||||
series[CADENCE_SERIES] = new ChartValueSeries(context,
|
||||
R.color.chart_heart_rate_border) {
|
||||
@Override
|
||||
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||
return chartPoint.getHeartRate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawIfChartPointHasNoData() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
seriesList.add(new ChartValueSeries(context,
|
||||
0,
|
||||
Integer.MAX_VALUE,
|
||||
new int[]{5, 10, 25, 50},
|
||||
R.string.description_sensor_cadence,
|
||||
R.string.description_sensor_cadence,
|
||||
R.color.chart_cadence_fill,
|
||||
R.color.chart_cadence_border);
|
||||
series[POWER_SERIES] = new ChartValueSeries(context,
|
||||
R.color.chart_cadence_border) {
|
||||
@Override
|
||||
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||
return chartPoint.getCadence();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawIfChartPointHasNoData() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
seriesList.add(new ChartValueSeries(context,
|
||||
0,
|
||||
1000,
|
||||
new int[]{5, 50, 100, 200},
|
||||
R.string.description_sensor_power,
|
||||
R.string.description_sensor_power,
|
||||
R.color.chart_power_fill,
|
||||
R.color.chart_power_border);
|
||||
R.color.chart_power_border) {
|
||||
@Override
|
||||
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||
return chartPoint.getPower();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawIfChartPointHasNoData() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
float scale = context.getResources().getDisplayMetrics().density;
|
||||
|
||||
@@ -279,6 +341,10 @@ public class ChartView extends View {
|
||||
setFocusable(true);
|
||||
setClickable(true);
|
||||
updateDimensions();
|
||||
|
||||
// either speedSeries or paceSeries should be enabled.
|
||||
speedSeries.setEnabled(reportSpeed);
|
||||
paceSeries.setEnabled(!reportSpeed);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -286,15 +352,6 @@ public class ChartView extends View {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the enabled value for a chart value series.
|
||||
*
|
||||
* @param index the chart value series index
|
||||
*/
|
||||
public void setChartValueSeriesEnabled(int index, boolean enabled) {
|
||||
series[index].setEnabled(enabled);
|
||||
}
|
||||
|
||||
public boolean getMetricUnits() {
|
||||
return metricUnits;
|
||||
}
|
||||
@@ -315,12 +372,30 @@ public class ChartView extends View {
|
||||
/**
|
||||
* Sets report speed.
|
||||
*
|
||||
* @param value true to report speed
|
||||
* @param value report speed (true) or pace (false)
|
||||
*/
|
||||
public void setReportSpeed(boolean value) {
|
||||
reportSpeed = value;
|
||||
}
|
||||
|
||||
public boolean applyReportSpeed() {
|
||||
if (reportSpeed) {
|
||||
if (!speedSeries.isEnabled()) {
|
||||
speedSeries.setEnabled(true);
|
||||
paceSeries.setEnabled(false);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (!paceSeries.isEnabled()) {
|
||||
speedSeries.setEnabled(false);
|
||||
paceSeries.setEnabled(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets show pointer.
|
||||
*
|
||||
@@ -335,15 +410,13 @@ public class ChartView extends View {
|
||||
*
|
||||
* @param dataPoints an array of data points to be added
|
||||
*/
|
||||
public void addPendingPoints(List<double[]> dataPoints) {
|
||||
public void addChartPoints(List<ChartPoint> dataPoints) {
|
||||
synchronized (chartData) {
|
||||
chartData.addAll(dataPoints);
|
||||
for (double[] dataPoint : dataPoints) {
|
||||
xExtremityMonitor.update(dataPoint[0]);
|
||||
for (int j = 0; j < series.length; j++) {
|
||||
if (!Double.isNaN(dataPoint[j + 1])) {
|
||||
series[j].update(dataPoint[j + 1]);
|
||||
}
|
||||
for (ChartPoint dataPoint : dataPoints) {
|
||||
xExtremityMonitor.update(dataPoint.getTimeOrDistance());
|
||||
for (ChartValueSeries i : seriesList) {
|
||||
i.update(dataPoint);
|
||||
}
|
||||
}
|
||||
updateDimensions();
|
||||
@@ -536,7 +609,7 @@ public class ChartView extends View {
|
||||
* @param canvas the canvas
|
||||
*/
|
||||
private void drawDataSeries(Canvas canvas) {
|
||||
for (ChartValueSeries chartValueSeries : series) {
|
||||
for (ChartValueSeries chartValueSeries : seriesList) {
|
||||
if (chartValueSeries.isEnabled() && chartValueSeries.hasData()) {
|
||||
chartValueSeries.drawPath(canvas);
|
||||
}
|
||||
@@ -602,9 +675,8 @@ public class ChartView extends View {
|
||||
int lines = titleDimensions[0];
|
||||
int lineHeight = titleDimensions[1];
|
||||
int count = 0;
|
||||
for (int i = 0; i < series.length; i++) {
|
||||
ChartValueSeries chartValueSeries = series[i];
|
||||
if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(i)) {
|
||||
for (ChartValueSeries chartValueSeries : seriesList) {
|
||||
if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(chartValueSeries)) {
|
||||
count++;
|
||||
String title = getContext().getString(chartValueSeries.getTitleId(metricUnits));
|
||||
Paint paint = chartValueSeries.getTitlePaint();
|
||||
@@ -622,9 +694,8 @@ public class ChartView extends View {
|
||||
private int[] getTitleDimensions() {
|
||||
int lines = 0;
|
||||
int lineHeight = 0;
|
||||
for (int i = 0; i < series.length; i++) {
|
||||
ChartValueSeries chartValueSeries = series[i];
|
||||
if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(i)) {
|
||||
for (ChartValueSeries chartValueSeries : seriesList) {
|
||||
if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(chartValueSeries)) {
|
||||
lines++;
|
||||
String title = getContext().getString(chartValueSeries.getTitleId(metricUnits));
|
||||
Rect rect = getRect(chartValueSeries.getTitlePaint(), title);
|
||||
@@ -728,10 +799,10 @@ public class ChartView extends View {
|
||||
canvas.drawLine(x, y, x, y + effectiveHeight, axisPaint);
|
||||
|
||||
int markerXPosition = x - spacer;
|
||||
for (int i = 0; i < series.length; i++) {
|
||||
int index = series.length - 1 - i;
|
||||
ChartValueSeries chartValueSeries = series[index];
|
||||
if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(index)) {
|
||||
for (int i = 0; i < seriesList.size(); i++) {
|
||||
int index = seriesList.size() - 1 - i;
|
||||
ChartValueSeries chartValueSeries = seriesList.get(index);
|
||||
if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(chartValueSeries)) {
|
||||
markerXPosition -= drawYAxisMarkers(chartValueSeries, canvas, markerXPosition) + spacer;
|
||||
}
|
||||
}
|
||||
@@ -779,18 +850,34 @@ public class ChartView extends View {
|
||||
* @param canvas the canvas
|
||||
*/
|
||||
private void drawPointer(Canvas canvas) {
|
||||
int index = -1;
|
||||
for (int i = 0; i < series.length; i++) {
|
||||
ChartValueSeries chartValueSeries = series[i];
|
||||
//TODO pass chartValueSeries to 2nd if
|
||||
// int index = -1;
|
||||
// for (int i = 0; i < seriesList.size(); i++) {
|
||||
// ChartValueSeries chartValueSeries = seriesList.get(i);
|
||||
// if (chartValueSeries.isEnabled() && chartValueSeries.hasData()) {
|
||||
// index = i;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if (index != -1 && chartData.size() > 0) {
|
||||
// int dx = getX(maxX) - pointer.getIntrinsicWidth() / 2;
|
||||
// int dy = getY(seriesList.get(index), chartData.get(chartData.size() - 1)[index + 1])
|
||||
// - pointer.getIntrinsicHeight();
|
||||
// canvas.translate(dx, dy);
|
||||
// pointer.draw(canvas);
|
||||
// }
|
||||
|
||||
ChartValueSeries firstChartValueSeries = null;
|
||||
for (ChartValueSeries chartValueSeries : seriesList) {
|
||||
if (chartValueSeries.isEnabled() && chartValueSeries.hasData()) {
|
||||
index = i;
|
||||
firstChartValueSeries = chartValueSeries;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index != -1 && chartData.size() > 0) {
|
||||
if (firstChartValueSeries != null && chartData.size() > 0) {
|
||||
int dx = getX(maxX) - pointer.getIntrinsicWidth() / 2;
|
||||
int dy = getY(series[index], chartData.get(chartData.size() - 1)[index + 1])
|
||||
- pointer.getIntrinsicHeight();
|
||||
double value = firstChartValueSeries.extractDataFromChartPoint(chartData.get(chartData.size() - 1));
|
||||
int dy = getY(firstChartValueSeries, value) - pointer.getIntrinsicHeight();
|
||||
canvas.translate(dx, dy);
|
||||
pointer.draw(canvas);
|
||||
}
|
||||
@@ -801,7 +888,7 @@ public class ChartView extends View {
|
||||
*/
|
||||
private void updatePaths() {
|
||||
synchronized (chartData) {
|
||||
for (ChartValueSeries chartValueSeries : series) {
|
||||
for (ChartValueSeries chartValueSeries : seriesList) {
|
||||
chartValueSeries.getPath().reset();
|
||||
}
|
||||
drawPaths();
|
||||
@@ -813,17 +900,19 @@ public class ChartView extends View {
|
||||
* Draws all paths.
|
||||
*/
|
||||
private void drawPaths() {
|
||||
boolean[] hasMoved = new boolean[series.length];
|
||||
boolean[] hasMoved = new boolean[seriesList.size()];
|
||||
|
||||
for (double[] dataPoint : chartData) {
|
||||
for (int j = 0; j < series.length; j++) {
|
||||
double value = dataPoint[j + 1];
|
||||
for (ChartPoint dataPoint : chartData) {
|
||||
for (int j = 0; j < seriesList.size(); j++) {
|
||||
ChartValueSeries chartValueSeries = seriesList.get(j);
|
||||
|
||||
|
||||
double value = chartValueSeries.extractDataFromChartPoint(dataPoint);
|
||||
if (Double.isNaN(value)) {
|
||||
continue;
|
||||
}
|
||||
ChartValueSeries chartValueSeries = series[j];
|
||||
Path path = chartValueSeries.getPath();
|
||||
int x = getX(dataPoint[0]);
|
||||
int x = getX(dataPoint.getTimeOrDistance());
|
||||
int y = getY(chartValueSeries, value);
|
||||
if (!hasMoved[j]) {
|
||||
hasMoved[j] = true;
|
||||
@@ -839,20 +928,21 @@ public class ChartView extends View {
|
||||
* Closes all paths.
|
||||
*/
|
||||
private void closePaths() {
|
||||
for (int i = 0; i < series.length; i++) {
|
||||
int first = getFirstPopulatedChartDataIndex(i);
|
||||
for (int i = 0; i < seriesList.size(); i++) {
|
||||
ChartValueSeries chartValueSeries = seriesList.get(i);
|
||||
int first = getFirstPopulatedChartDataIndex(chartValueSeries);
|
||||
|
||||
if (first != -1) {
|
||||
int xCorner = getX(chartData.get(first)[0]);
|
||||
int xCorner = getX(chartData.get(first).getTimeOrDistance());
|
||||
int yCorner = topBorder + effectiveHeight;
|
||||
ChartValueSeries chartValueSeries = series[i];
|
||||
Path path = chartValueSeries.getPath();
|
||||
// Bottom right corner
|
||||
path.lineTo(getX(chartData.get(chartData.size() - 1)[0]), yCorner);
|
||||
path.lineTo(getX(chartData.get(chartData.size() - 1).getTimeOrDistance()), yCorner);
|
||||
// Bottom left corner
|
||||
path.lineTo(xCorner, yCorner);
|
||||
// Top right corner
|
||||
path.lineTo(xCorner, getY(chartValueSeries, chartData.get(first)[i + 1]));
|
||||
double value = chartValueSeries.extractDataFromChartPoint(chartData.get(first));
|
||||
path.lineTo(xCorner, getY(chartValueSeries, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -860,12 +950,10 @@ public class ChartView extends View {
|
||||
/**
|
||||
* Finds the index of the first data point containing data for a series.
|
||||
* Returns -1 if no data point contains data for the series.
|
||||
*
|
||||
* @param seriesIndex the series's index
|
||||
*/
|
||||
private int getFirstPopulatedChartDataIndex(int seriesIndex) {
|
||||
private int getFirstPopulatedChartDataIndex(ChartValueSeries chartValueSeries) {
|
||||
for (int i = 0; i < chartData.size(); i++) {
|
||||
if (!Double.isNaN(chartData.get(i)[seriesIndex + 1])) {
|
||||
if (chartValueSeries.isChartPointValid(chartData.get(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -877,7 +965,7 @@ public class ChartView extends View {
|
||||
*/
|
||||
private void updateDimensions() {
|
||||
maxX = xExtremityMonitor.hasData() ? xExtremityMonitor.getMax() : 1.0;
|
||||
for (ChartValueSeries chartValueSeries : series) {
|
||||
for (ChartValueSeries chartValueSeries : seriesList) {
|
||||
chartValueSeries.updateDimension();
|
||||
}
|
||||
float density = getContext().getResources().getDisplayMetrics().density;
|
||||
@@ -885,9 +973,9 @@ public class ChartView extends View {
|
||||
yAxisOffset = (int) (density * Y_AXIS_OFFSET);
|
||||
|
||||
int markerLength = 0;
|
||||
for (int i = 0; i < series.length; i++) {
|
||||
ChartValueSeries chartValueSeries = series[i];
|
||||
if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(i)) {
|
||||
for (int i = 0; i < seriesList.size(); i++) {
|
||||
ChartValueSeries chartValueSeries = seriesList.get(i);
|
||||
if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(chartValueSeries)) {
|
||||
Rect rect = getRect(chartValueSeries.getMarkerPaint(), chartValueSeries.getLargestMarker());
|
||||
markerLength += rect.width() + spacer;
|
||||
}
|
||||
@@ -980,22 +1068,12 @@ public class ChartView extends View {
|
||||
|
||||
/**
|
||||
* Returns true if the index is allowed when the chartData is empty.
|
||||
*
|
||||
* @param index the index
|
||||
*/
|
||||
private boolean allowIfEmpty(int index) {
|
||||
private boolean allowIfEmpty(ChartValueSeries chartValueSeries) {
|
||||
if (!chartData.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
switch (index) {
|
||||
case ELEVATION_SERIES:
|
||||
return true;
|
||||
case SPEED_SERIES:
|
||||
return reportSpeed;
|
||||
case PACE_SERIES:
|
||||
return !reportSpeed;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return chartValueSeries.drawIfChartPointHasNoData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,18 +33,17 @@ import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TrackDetailActivity;
|
||||
import de.dennisguse.opentracks.chart.ChartPoint;
|
||||
import de.dennisguse.opentracks.chart.ChartView;
|
||||
import de.dennisguse.opentracks.content.TrackDataHub;
|
||||
import de.dennisguse.opentracks.content.TrackDataListener;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||
import de.dennisguse.opentracks.util.LocationUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
|
||||
/**
|
||||
* A fragment to display track chart to the user.
|
||||
@@ -66,7 +65,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
return chartFragment;
|
||||
}
|
||||
|
||||
private final List<double[]> pendingPoints = new ArrayList<>();
|
||||
private final List<ChartPoint> pendingPoints = new ArrayList<>();
|
||||
|
||||
private TrackDataHub trackDataHub;
|
||||
|
||||
@@ -74,11 +73,11 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
private TrackStatisticsUpdater trackStatisticsUpdater;
|
||||
private long startTime;
|
||||
|
||||
//TODO Why is this needed?
|
||||
private int recordingDistanceInterval;
|
||||
|
||||
// Modes of operation
|
||||
private boolean chartByDistance;
|
||||
private final boolean[] chartShow = new boolean[]{true, true, true, true, true, true};
|
||||
|
||||
// UI elements
|
||||
private ChartView chartView;
|
||||
@@ -104,8 +103,8 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext());
|
||||
if (reportSpeed != chartView.getReportSpeed()) {
|
||||
chartView.setReportSpeed(reportSpeed);
|
||||
setSeriesEnabled(ChartView.SPEED_SERIES, reportSpeed);
|
||||
setSeriesEnabled(ChartView.PACE_SERIES, !reportSpeed);
|
||||
chartView.applyReportSpeed();
|
||||
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -236,7 +235,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
@Override
|
||||
public void onNewTrackPointsDone() {
|
||||
if (isResumed()) {
|
||||
chartView.addPendingPoints(pendingPoints);
|
||||
chartView.addChartPoints(pendingPoints);
|
||||
pendingPoints.clear();
|
||||
runOnUiThread(updateChart);
|
||||
}
|
||||
@@ -267,37 +266,12 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
* Checks the chart settings.
|
||||
*/
|
||||
private void checkChartSettings() {
|
||||
boolean needUpdate = false;
|
||||
|
||||
if (setSeriesEnabled(ChartView.SPEED_SERIES, chartView.getReportSpeed())) {
|
||||
needUpdate = true;
|
||||
}
|
||||
if (setSeriesEnabled(ChartView.PACE_SERIES, !chartView.getReportSpeed())) {
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
boolean needUpdate = chartView.applyReportSpeed();
|
||||
if (needUpdate) {
|
||||
chartView.postInvalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the series enabled value.
|
||||
*
|
||||
* @param index the series index
|
||||
* @param value the value
|
||||
* @return true if changed
|
||||
*/
|
||||
private boolean setSeriesEnabled(int index, boolean value) {
|
||||
if (chartShow[index] != value) {
|
||||
chartShow[index] = value;
|
||||
chartView.setChartValueSeriesEnabled(index, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the trackDataHub.
|
||||
* Needs to be synchronized because trackDataHub can be accessed by multiple threads.
|
||||
@@ -346,68 +320,10 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a {@link TrackPoint}, fill in a data point. <br>
|
||||
* data[0] = time/distance <br>
|
||||
* data[1] = elevation <br>
|
||||
* data[2] = speed <br>
|
||||
* data[3] = pace <br>
|
||||
* data[4] = heart rate <br>
|
||||
* data[5] = cadence <br>
|
||||
* data[6] = power <br>
|
||||
*
|
||||
* @param trackPoint the trackPoint
|
||||
*/
|
||||
@VisibleForTesting
|
||||
double[] createPendingPoint(@NonNull TrackPoint trackPoint) {
|
||||
double timeOrDistance = Double.NaN;
|
||||
double elevation = Double.NaN;
|
||||
double speed = Double.NaN;
|
||||
double pace = Double.NaN;
|
||||
|
||||
if (trackStatisticsUpdater != null) {
|
||||
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
|
||||
TrackStatistics trackStatistics = trackStatisticsUpdater.getTrackStatistics();
|
||||
if (chartByDistance) {
|
||||
double distance = trackStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||
if (!chartView.getMetricUnits()) {
|
||||
distance *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
timeOrDistance = distance;
|
||||
} else {
|
||||
timeOrDistance = trackStatistics.getTotalTime();
|
||||
}
|
||||
|
||||
elevation = trackStatisticsUpdater.getSmoothedElevation();
|
||||
if (!chartView.getMetricUnits()) {
|
||||
elevation *= UnitConversions.M_TO_FT;
|
||||
}
|
||||
|
||||
speed = trackStatisticsUpdater.getSmoothedSpeed() * UnitConversions.MS_TO_KMH;
|
||||
if (!chartView.getMetricUnits()) {
|
||||
speed *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
pace = speed == 0 ? 0.0 : 60.0 / speed;
|
||||
}
|
||||
|
||||
double heartRate = Double.NaN;
|
||||
double cadence = Double.NaN;
|
||||
double power = Double.NaN;
|
||||
if (trackPoint.getSensorDataSet() != null) {
|
||||
SensorDataSet sensorDataSet = trackPoint.getSensorDataSet();
|
||||
if (sensorDataSet.hasHeartRate()) {
|
||||
heartRate = sensorDataSet.getHeartRate();
|
||||
}
|
||||
if (sensorDataSet.hasCadence()) {
|
||||
cadence = sensorDataSet.getCadence();
|
||||
}
|
||||
if (sensorDataSet.hasPower()) {
|
||||
power = sensorDataSet.getPower();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Is related to ChartView.ELEVATION_SERIES etc.
|
||||
return new double[]{timeOrDistance, elevation, speed, pace, heartRate, cadence, power};
|
||||
ChartPoint createPendingPoint(@NonNull TrackPoint trackPoint) {
|
||||
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
|
||||
return new ChartPoint(trackStatisticsUpdater, trackPoint.getSensorDataSet(), chartByDistance, chartView.getMetricUnits());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -61,9 +61,9 @@ public class TrackStatisticsUpdater {
|
||||
private final TrackStatistics trackStatistics;
|
||||
|
||||
// A buffer of the recent elevation readings (m)
|
||||
private final DoubleBuffer elevationBuffer = new DoubleBuffer(ELEVATION_SMOOTHING_FACTOR);
|
||||
private final DoubleBuffer elevationBuffer_m = new DoubleBuffer(ELEVATION_SMOOTHING_FACTOR);
|
||||
// A buffer of the recent speed readings (m/s) for calculating max speed
|
||||
private final DoubleBuffer speedBuffer = new DoubleBuffer(SPEED_SMOOTHING_FACTOR);
|
||||
private final DoubleBuffer speedBuffer_ms = new DoubleBuffer(SPEED_SMOOTHING_FACTOR);
|
||||
|
||||
// The current segment's statistics
|
||||
private TrackStatistics currentSegment;
|
||||
@@ -128,8 +128,8 @@ public class TrackStatisticsUpdater {
|
||||
currentSegment = init(trackPoint.getLocation().getTime());
|
||||
lastTrackPoint = null;
|
||||
lastMovingTrackPoint = null;
|
||||
elevationBuffer.reset();
|
||||
speedBuffer.reset();
|
||||
elevationBuffer_m.reset();
|
||||
speedBuffer_ms.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ public class TrackStatisticsUpdater {
|
||||
|
||||
double movingDistance = lastMovingTrackPoint.distanceTo(trackPoint);
|
||||
if (movingDistance < minRecordingDistance && (!trackPoint.hasSpeed() || trackPoint.getSpeed() < MAX_NO_MOVEMENT_SPEED)) {
|
||||
speedBuffer.reset();
|
||||
speedBuffer_ms.reset();
|
||||
lastTrackPoint = trackPoint;
|
||||
return;
|
||||
}
|
||||
@@ -181,11 +181,11 @@ public class TrackStatisticsUpdater {
|
||||
* The elevation readings is noisy so the smoothed elevation is better than the raw elevation for many tasks.
|
||||
*/
|
||||
public double getSmoothedElevation() {
|
||||
return elevationBuffer.getAverage();
|
||||
return elevationBuffer_m.getAverage();
|
||||
}
|
||||
|
||||
public double getSmoothedSpeed() {
|
||||
return speedBuffer.getAverage();
|
||||
return speedBuffer_ms.getAverage();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,11 +199,11 @@ public class TrackStatisticsUpdater {
|
||||
@VisibleForTesting
|
||||
private void updateSpeed(long time, double speed, long lastLocationTime, double lastLocationSpeed) {
|
||||
if (speed < MAX_NO_MOVEMENT_SPEED) {
|
||||
speedBuffer.reset();
|
||||
speedBuffer_ms.reset();
|
||||
} else if (isValidSpeed(time, speed, lastLocationTime, lastLocationSpeed)) {
|
||||
speedBuffer.setNext(speed);
|
||||
if (speedBuffer.getAverage() > currentSegment.getMaxSpeed()) {
|
||||
currentSegment.setMaxSpeed(speedBuffer.getAverage());
|
||||
speedBuffer_ms.setNext(speed);
|
||||
if (speedBuffer_ms.getAverage() > currentSegment.getMaxSpeed()) {
|
||||
currentSegment.setMaxSpeed(speedBuffer_ms.getAverage());
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Invalid speed. speed: " + speed + " lastLocationSpeed: " + lastLocationSpeed);
|
||||
@@ -218,9 +218,9 @@ public class TrackStatisticsUpdater {
|
||||
@VisibleForTesting
|
||||
private double updateElevation(double elevation) {
|
||||
// Update elevation using the smoothed average
|
||||
double oldAverage = elevationBuffer.getAverage();
|
||||
elevationBuffer.setNext(elevation);
|
||||
double newAverage = elevationBuffer.getAverage();
|
||||
double oldAverage = elevationBuffer_m.getAverage();
|
||||
elevationBuffer_m.setNext(elevation);
|
||||
double newAverage = elevationBuffer_m.getAverage();
|
||||
|
||||
currentSegment.updateElevationExtremities(newAverage);
|
||||
double difference = newAverage - oldAverage;
|
||||
@@ -264,8 +264,8 @@ public class TrackStatisticsUpdater {
|
||||
}
|
||||
|
||||
// Only check if the speed buffer is full. Check that the speed is less than 10X the smoothed average and the speed difference doesn't imply 2g acceleration.
|
||||
if (speedBuffer.isFull()) {
|
||||
double average = speedBuffer.getAverage();
|
||||
if (speedBuffer_ms.isFull()) {
|
||||
double average = speedBuffer_ms.getAverage();
|
||||
double diff = Math.abs(average - speed);
|
||||
return (speed < average * 10) && (diff < MAX_ACCELERATION * timeDifference);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user