File: CreateMarkerWaypointsFunction.java

package info (click to toggle)
gpsprune 26.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,824 kB
  • sloc: java: 52,154; sh: 25; makefile: 21; python: 15
file content (274 lines) | stat: -rw-r--r-- 9,026 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
package tim.prune.function;

import java.util.ArrayList;

import tim.prune.App;
import tim.prune.I18nManager;
import tim.prune.cmd.AppendRangeCmd;
import tim.prune.config.Config;
import tim.prune.data.DataPoint;
import tim.prune.data.RangeStats;
import tim.prune.data.UnitSetLibrary;

/**
 * Function to create waypoints marking regular distance intervals,
 * regular time intervals, or halfway points
 */
public class CreateMarkerWaypointsFunction extends DistanceTimeLimitFunction
{
	/** ArrayList of points to append to the track */
	private final ArrayList<DataPoint> _pointsToAdd = new ArrayList<>();
	/** Counter of previously used multiple */
	private int _previousMultiple = 0;

	/*
	 * Type of halfway point
	 */
	private enum HalfwayType
	{
		HALF_DISTANCE,
		HALF_CLIMB,
		HALF_DESCENT
	}

	/**
	 * Constructor
	 */
	public CreateMarkerWaypointsFunction(App inApp) {
		super(inApp, true);
	}

	/**
	 * @return name key
	 */
	public String getNameKey() {
		return "function.createmarkerwaypoints";
	}

	/**
	 * Init the state to start collecting a new set of points
	 */
	private void initMemory()
	{
		_pointsToAdd.clear();
		_previousMultiple = 0;
	}

	/**
	 * The dialog has been completed and OK pressed, so do the point creation
	 */
	protected void performFunction()
	{
		// Determine which kind of markers to create
		final int timeLimitSeconds = getTimeLimitInSeconds();
		final boolean createByTime = (timeLimitSeconds > 0);
		final double distLimitKm = getDistanceLimitKilometres();
		final boolean createByDistance = (distLimitKm > 0.0);
		final boolean createHalves = isHalvesSelected();

		// set up the memory from scratch to collect the created points
		initMemory();

		if (createByTime || createByDistance) {
			createWaypointsAtIntervals(timeLimitSeconds, distLimitKm);
		}
		else if (createHalves) {
			createHalfwayWaypoints();
		}
		else {
			return;
		}

		if (!_pointsToAdd.isEmpty())
		{
			AppendRangeCmd command = new AppendRangeCmd(_pointsToAdd);
			command.setDescription(getName());
			final String confirmMessage = I18nManager.getTextWithNumber("confirm.pointsadded", _pointsToAdd.size());
			command.setConfirmText(confirmMessage);
			_app.execute(command);
		}
		_dialog.dispose();
	}

	/**
	 * Create waypoints according to the given intervals
	 * @param inTimeLimitSeconds time limit in seconds
	 * @param inDistLimitKm distance limit in kilometres
	 */
	private void createWaypointsAtIntervals(int inTimeLimitSeconds, double inDistLimitKm)
	{
		final boolean createByTime = (inTimeLimitSeconds > 0);
		final boolean createByDistance = (inDistLimitKm > 0.0);

		// Make new waypoints, looping through the points in the track
		DataPoint prevPoint = null;
		double currValue = 0.0, prevValue = 0.0;
		final int altitudeTolerance = getConfig().getConfigInt(Config.KEY_ALTITUDE_TOLERANCE) / 100;
		RangeStats rangeStats = new RangeStats(altitudeTolerance);
		final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
		for (int i=0; i<numPoints; i++)
		{
			DataPoint currPoint = _app.getTrackInfo().getTrack().getPoint(i);
			rangeStats.addPoint(currPoint);

			if (!currPoint.isWaypoint())
			{
				// Calculate current value
				if (createByTime)
				{
					currValue = rangeStats.getMovingDurationInSeconds();
					processValue(prevPoint, prevValue, inTimeLimitSeconds, currPoint, currValue);
				}
				else if (createByDistance)
				{
					currValue = rangeStats.getMovingDistanceKilometres();
					processValue(prevPoint, prevValue, inDistLimitKm, currPoint, currValue);
				}

				prevPoint = currPoint;
				prevValue = currValue;
			}
		}
	}

	/**
	 * Consider a pair of points in the track to see if a new marker should be inserted between them
	 * @param inPrevPoint previous point
	 * @param inPrevValue value of function at this previous point
	 * @param inLimit user-specified limit for marker values
	 * @param inCurrPoint current point
	 * @param inCurrValue value of function at this current point
	 */
	private void processValue(DataPoint inPrevPoint, double inPrevValue, double inLimit,
		DataPoint inCurrPoint, double inCurrValue)
	{
		// Check the current multiple and compare with previously used one
		final int currMultiple = (int) Math.floor(inCurrValue / inLimit);
		for (int m=_previousMultiple+1; m<=currMultiple; m++)
		{
			// Calculate position of limit between the two points
			final double valueBeforeBreak = (m * inLimit) - inPrevValue;
			final double valueAfterBreak = inCurrValue - (m * inLimit);
			final double fractionFromPrev = valueBeforeBreak / (valueBeforeBreak + valueAfterBreak);
			DataPoint marker = PointUtils.interpolate(inPrevPoint, inCurrPoint, fractionFromPrev);
			marker.setWaypointName(createLimitDescription(m));
			_pointsToAdd.add(marker);
		}
		_previousMultiple = currMultiple;
	}

	/**
	 * Create waypoints for the halfway markers
	 */
	private void createHalfwayWaypoints()
	{
		// Calculate the details of the whole track so we can see what to halve
		final int numPoints = _app.getTrackInfo().getTrack().getNumPoints();
		final int altitudeTolerance = getConfig().getConfigInt(Config.KEY_ALTITUDE_TOLERANCE) / 100;
		RangeStats totalStats = new RangeStats(altitudeTolerance);
		for (int i=0; i<numPoints; i++)
		{
			DataPoint currPoint = _app.getTrackInfo().getTrack().getPoint(i);
			totalStats.addPoint(currPoint);
		}
		// Calculate total moving distance of track
		final double totalDist = totalStats.getMovingDistanceKilometres();
		// If the track has altitudes, also calculate total climb and total descent
		final double totalClimb = totalStats.getMovingAltitudeRange().getClimb(UnitSetLibrary.UNITS_METRES);
		final double totalDescent = totalStats.getMovingAltitudeRange().getDescent(UnitSetLibrary.UNITS_METRES);

		final double halfDistance = totalDist / 2.0;
		final double halfClimb = totalClimb / 2.0;
		final double halfDescent = totalDescent / 2.0;

		// Now loop through points again, looking for the halfway points
		RangeStats partialStats = new RangeStats(altitudeTolerance);
		DataPoint prevPoint = null;
		boolean createdDistance = false, createdClimb = false, createdDescent = false;
		double prevDistance = 0.0, prevClimb = 0.0, prevDescent = 0.0;
		for (int i=0; i<numPoints; i++)
		{
			DataPoint currPoint = _app.getTrackInfo().getTrack().getPoint(i);
			partialStats.addPoint(currPoint);
			if (!currPoint.isWaypoint())
			{
				// distance
				if (!createdDistance && totalDist > 0.0)
				{
					final double currDist = partialStats.getMovingDistanceKilometres();
					createdDistance = processHalfValue(prevPoint, prevDistance, halfDistance,
						currPoint, currDist, HalfwayType.HALF_DISTANCE);
					prevDistance = currDist;
				}
				// climb
				if (!createdClimb && totalClimb > 0.0)
				{
					final double currClimb = partialStats.getMovingAltitudeRange().getClimb(UnitSetLibrary.UNITS_METRES);
					createdClimb = processHalfValue(prevPoint, prevClimb, halfClimb,
						currPoint, currClimb, HalfwayType.HALF_CLIMB);
					prevClimb = currClimb;
				}
				// descent
				if (!createdDescent && totalDescent > 0.0)
				{
					final double currDescent = partialStats.getMovingAltitudeRange().getDescent(UnitSetLibrary.UNITS_METRES);
					createdDescent = processHalfValue(prevPoint, prevDescent, halfDescent,
						currPoint, currDescent, HalfwayType.HALF_DESCENT);
					prevDescent = currDescent;
				}

				prevPoint = currPoint;
			}
		}
	}

	/**
	 * Consider a pair of points in the track to see if a new halfway marker should be inserted between them
	 * @param inPrevPoint previous point
	 * @param inPrevValue value of function at this previous point
	 * @param inTargetValue target halfway value
	 * @param inCurrPoint current point
	 * @param inCurrValue value of function at this current point
	 * @param inType type of halfway point
	 */
	private boolean processHalfValue(DataPoint inPrevPoint, double inPrevValue, double inTargetValue,
		DataPoint inCurrPoint, double inCurrValue, HalfwayType inType)
	{
		if (inPrevValue <= inTargetValue && inCurrValue >= inTargetValue)
		{
			// Calculate position of limit between the two points
			final double valueBeforeBreak = inTargetValue - inPrevValue;
			final double valueAfterBreak = inCurrValue - inTargetValue;
			final double fractionFromPrev = valueBeforeBreak / (valueBeforeBreak + valueAfterBreak);
			DataPoint marker = PointUtils.interpolate(inPrevPoint, inCurrPoint, fractionFromPrev);
			marker.setWaypointName(createHalfwayName(inType));
			_pointsToAdd.add(marker);
			return true;
		}
		return false;
	}

	/**
	 * Create the name of the halfway point according to type
	 * @param inType type of point
	 */
	private String createHalfwayName(HalfwayType inType)
	{
		final String typeString;
		switch (inType)
		{
			case HALF_DISTANCE:
				typeString = "distance";
				break;
			case HALF_CLIMB:
				typeString = "climb";
				break;
			case HALF_DESCENT:
				typeString = "descent";
				break;
			default:
				return "half";
		}
		return I18nManager.getText("dialog.markers.half." + typeString);
	}
}