--- /dev/null
+package tim.prune.function.compress;
+
+import tim.prune.App;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.data.DataPoint;
+import tim.prune.data.Distance;
+import tim.prune.data.RangeStats;
+import tim.prune.data.Track;
+import tim.prune.data.UnitSetLibrary;
+
+/**
+ * Function to mark all the points going uphill on ski lifts
+ */
+public class MarkLiftsFunction extends MarkAndDeleteFunction
+{
+ /**
+ * Constructor
+ * @param inApp App object
+ */
+ public MarkLiftsFunction(App inApp)
+ {
+ super(inApp);
+ }
+
+ /** @return name key */
+ public String getNameKey() {
+ return "function.marklifts";
+ }
+
+ /** this function _does_ require split at deleted points */
+ protected boolean getShouldSplitSegments() {
+ return true;
+ }
+
+ /**
+ * Begin the function using the set parameters
+ */
+ public void begin()
+ {
+ // TODO: Might need to do this in a separate thread, it might take a while if the track is big
+ // Loop over all points in track
+ int numMarked = 0;
+ final Track track = _app.getTrackInfo().getTrack();
+ final int numPoints = track.getNumPoints();
+ boolean[] markFlags = new boolean[numPoints];
+ int previousStartIndex = -1;
+ for (int i=0; i<numPoints; i++)
+ {
+ DataPoint currPoint = track.getPoint(i);
+ if (currPoint != null && !currPoint.isWaypoint() && currPoint.hasAltitude() && currPoint.hasTimestamp())
+ {
+ int n = i+1;
+ DataPoint endPoint = track.getPoint(n);
+ while (endPoint != null && endPoint.hasAltitude() && endPoint.hasTimestamp() &&
+ !endPoint.isWaypoint() && endPoint.getTimestamp().getSecondsSince(currPoint.getTimestamp()) < 120)
+ {
+ n++;
+ endPoint = track.getPoint(n);
+ }
+ if (endPoint != null && endPoint.hasAltitude() && endPoint.hasTimestamp() && !endPoint.isWaypoint()
+ && n > (i+10))
+ {
+ // Found a 2 minute range to test with at least 12 points
+ if (looksLikeLiftRange(track, i, n))
+ {
+ // Passes tests, so we want to mark all points between i and n
+ int startIndex = i;
+ // First check if we can merge with the previous marked range
+ if (previousStartIndex >= 0
+ && (looksLikeLiftRange(track, previousStartIndex, i)
+ || looksLikeLiftRange(track, previousStartIndex, n)))
+ {
+ startIndex = previousStartIndex; // merge
+ }
+ for (int j=startIndex; j<=n; j++)
+ {
+ markFlags[j] = true;
+ }
+ // Remember start point for next one
+ previousStartIndex = startIndex;
+ // skip forward half the range, don't need to test the same points again
+ i = (i+n)/2;
+ }
+ }
+ }
+ }
+
+ // Copy mark flags to points
+ for (int i=0; i<numPoints; i++)
+ {
+ DataPoint point = track.getPoint(i);
+ if (!point.isWaypoint()) point.setMarkedForDeletion(markFlags[i]);
+ if (markFlags[i]) numMarked++;
+ }
+ // Inform subscribers to update display
+ UpdateMessageBroker.informSubscribers();
+ // Confirm message showing how many marked
+ if (numMarked > 0)
+ {
+ optionallyDeleteMarkedPoints(numMarked);
+ }
+ else
+ {
+ // TODO: Show message that no lifts were found
+ }
+ }
+
+
+ /**
+ * Check whether the specified range looks like an uphill lift section or not
+ * Must go at most a little bit downhill, much more uphill than down, and straight
+ * (speed isn't checked yet, but maybe could be?)
+ * @param inTrack track
+ * @param inStartIndex start index of range
+ * @param inEndIndex end index of range
+ * @return true if it looks like a lift
+ */
+ private boolean looksLikeLiftRange(Track inTrack, int inStartIndex, int inEndIndex)
+ {
+ RangeStats stats = new RangeStats(inTrack, inStartIndex, inEndIndex);
+ int descent = stats.getTotalAltitudeRange().getDescent(UnitSetLibrary.UNITS_METRES);
+ if (descent < 20)
+ {
+ int ascent = stats.getTotalAltitudeRange().getClimb(UnitSetLibrary.UNITS_METRES);
+ if (ascent > (descent * 10))
+ {
+ // Now check distance and compare to distance between start and end
+ final DataPoint startPoint = inTrack.getPoint(inStartIndex);
+ final DataPoint endPoint = inTrack.getPoint(inEndIndex);
+
+ final double trackDist = stats.getTotalDistance();
+ final double endToEndDist = Distance.convertRadiansToDistance(
+ DataPoint.calculateRadiansBetween(startPoint, endPoint));
+ if ((trackDist / endToEndDist) < 1.02) // Straight(ish) line
+ {
+ return true;
+ }
+ //else System.out.println("Not straight enough: " + (trackDist / endToEndDist));
+ }
+ }
+ return false;
+ }
+}