1 package tim.prune.data;
3 import tim.prune.DataSubscriber;
4 import tim.prune.UpdateMessageBroker;
7 * Class to represent a selected portion of a Track
10 public class Selection
12 private Track _track = null;
13 private int _currentPoint = -1;
14 private boolean _valid = false;
15 private int _prevNumPoints = 0;
16 private int _startIndex = -1, _endIndex = -1;
17 private int _currentPhotoIndex = -1;
18 private int _currentAudioIndex = -1;
19 private IntegerRange _altitudeRange = null;
20 private int _climb = -1, _descent = -1;
21 private Altitude.Format _altitudeFormat = Altitude.Format.NO_FORMAT;
22 private long _totalSeconds = 0L, _movingSeconds = 0L;
23 private double _angDistance = -1.0, _angMovingDistance = -1.0;
24 private int _numSegments = 0;
29 * @param inTrack track object
31 public Selection(Track inTrack)
38 * Mark selection invalid so it will be recalculated
40 public void markInvalid()
47 * @return the current point index
49 public int getCurrentPointIndex()
56 * @return true if range is selected
58 public boolean hasRangeSelected()
60 return _startIndex >= 0 && _endIndex > _startIndex;
65 * Recalculate all selection details
67 private void recalculate()
69 _altitudeFormat = Altitude.Format.NO_FORMAT;
71 final int numPoints = _track.getNumPoints();
72 // Recheck if the number of points has changed
73 if (numPoints != _prevNumPoints) {
74 _prevNumPoints = numPoints;
77 if (numPoints > 0 && hasRangeSelected())
79 _altitudeRange = new IntegerRange();
82 Altitude altitude = null;
83 Timestamp time = null, startTime = null, endTime = null;
84 Timestamp previousTime = null;
85 DataPoint lastPoint = null, currPoint = null;
86 _angDistance = 0.0; _angMovingDistance = 0.0;
87 _totalSeconds = 0L; _movingSeconds = 0L;
90 boolean foundAlt = false;
91 for (int i=_startIndex; i<=_endIndex; i++)
93 currPoint = _track.getPoint(i);
94 altitude = currPoint.getAltitude();
95 // Ignore waypoints in altitude calculations
96 if (!currPoint.isWaypoint() && altitude.isValid())
98 altValue = altitude.getValue(_altitudeFormat);
99 if (_altitudeFormat == Altitude.Format.NO_FORMAT)
100 _altitudeFormat = altitude.getFormat();
101 _altitudeRange.addValue(altValue);
104 if (altValue > lastAltValue)
105 _climb += (altValue - lastAltValue);
107 _descent += (lastAltValue - altValue);
109 lastAltValue = altValue;
112 // Store the first and last timestamp in the range
113 time = currPoint.getTimestamp();
116 if (startTime == null || startTime.isAfter(time)) startTime = time;
117 if (endTime == null || time.isAfter(endTime)) endTime = time;
119 if (!currPoint.getSegmentStart() && previousTime != null && time.isAfter(previousTime)) {
120 _movingSeconds += time.getSecondsSince(previousTime);
124 // Calculate distances, again ignoring waypoints
125 if (!currPoint.isWaypoint())
127 if (lastPoint != null)
129 double radians = DataPoint.calculateRadiansBetween(lastPoint, currPoint);
130 _angDistance += radians;
131 if (currPoint.getSegmentStart()) {
135 _angMovingDistance += radians;
138 lastPoint = currPoint;
139 // If it's a track point then there must be at least one segment
140 if (_numSegments == 0) {_numSegments = 1;}
143 if (endTime != null) {
144 _totalSeconds = endTime.getSecondsSince(startTime);
152 * @return start index
154 public int getStart()
156 if (!_valid) recalculate();
166 if (!_valid) recalculate();
172 * @return the altitude format, ie feet or metres
174 public Altitude.Format getAltitudeFormat()
176 return _altitudeFormat;
180 * @return altitude range
182 public IntegerRange getAltitudeRange()
184 if (!_valid) recalculate();
185 return _altitudeRange;
192 public int getClimb()
194 if (!_valid) recalculate();
201 public int getDescent()
203 if (!_valid) recalculate();
209 * @return number of seconds spanned by selection
211 public long getNumSeconds()
213 if (!_valid) recalculate();
214 return _totalSeconds;
218 * @return number of seconds spanned by segments within selection
220 public long getMovingSeconds()
222 if (!_valid) recalculate();
223 return _movingSeconds;
227 * @param inUnits distance units to use, from class Distance
228 * @return distance of Selection in specified units
230 public double getDistance(Distance.Units inUnits)
232 return Distance.convertRadiansToDistance(_angDistance, inUnits);
236 * @param inUnits distance units to use, from class Distance
237 * @return moving distance of Selection in specified units
239 public double getMovingDistance(Distance.Units inUnits)
241 return Distance.convertRadiansToDistance(_angMovingDistance, inUnits);
245 * @return number of segments in selection
247 public int getNumSegments()
253 * Clear selected point, range, photo and audio
255 public void clearAll()
259 _currentPhotoIndex = -1;
260 _currentAudioIndex = -1;
266 * Select range from start to end
267 * @param inStartIndex index of start of range
268 * @param inEndIndex index of end of range
270 public void selectRange(int inStartIndex, int inEndIndex)
272 _startIndex = inStartIndex;
273 _endIndex = inEndIndex;
280 * Select the range from the current point
282 public void selectRangeStart()
284 selectRangeStart(_currentPoint);
289 * Set the index for the start of the range selection
290 * @param inStartIndex start index
292 private void selectRangeStart(int inStartIndex)
294 if (inStartIndex < 0)
296 _startIndex = _endIndex = -1;
300 _startIndex = inStartIndex;
301 // Move end of selection to max if necessary
302 if (_endIndex <= _startIndex)
304 _endIndex = _track.getNumPoints() - 1;
308 UpdateMessageBroker.informSubscribers();
313 * Select the range up to the current point
315 public void selectRangeEnd()
317 selectRangeEnd(_currentPoint);
322 * Set the index for the end of the range selection
323 * @param inEndIndex end index
325 public void selectRangeEnd(int inEndIndex)
329 _startIndex = _endIndex = -1;
333 _endIndex = inEndIndex;
334 // Move start of selection to min if necessary
335 if (_startIndex > _endIndex || _startIndex < 0) {
340 UpdateMessageBroker.informSubscribers();
345 * Modify the selection given that the selected range has been deleted
347 public void modifyRangeDeleted()
349 // Modify current point, if any
350 if (_currentPoint > _endIndex)
352 _currentPoint -= (_endIndex - _startIndex);
354 else if (_currentPoint > _startIndex)
356 _currentPoint = _startIndex;
358 // Clear selected range
359 _startIndex = _endIndex = -1;
360 // Check for consistency and fire update
366 * Modify the selection when a point is deleted
368 public void modifyPointDeleted()
370 // current point index doesn't change, just gets checked
371 // range needs to get altered if deleted point is inside or before
372 if (hasRangeSelected() && _currentPoint <= _endIndex)
375 if (_currentPoint < _startIndex)
384 * Select the specified photo and point
385 * @param inPointIndex index of selected point
386 * @param inPhotoIndex index of selected photo in PhotoList
387 * @param inAudioIndex index of selected audio item
389 public void selectPointPhotoAudio(int inPointIndex, int inPhotoIndex, int inAudioIndex)
391 _currentPoint = inPointIndex;
392 _currentPhotoIndex = inPhotoIndex;
393 _currentAudioIndex = inAudioIndex;
399 * @return currently selected photo index
401 public int getCurrentPhotoIndex()
403 return _currentPhotoIndex;
407 * @return currently selected audio index
409 public int getCurrentAudioIndex()
411 return _currentAudioIndex;
415 * Check that the selection still makes sense
416 * and fire update message to listeners
420 if (_track != null && _track.getNumPoints() > 0)
422 int maxIndex = _track.getNumPoints() - 1;
423 if (_currentPoint > maxIndex)
425 _currentPoint = maxIndex;
427 if (_endIndex > maxIndex)
429 _endIndex = maxIndex;
431 if (_startIndex > maxIndex)
433 _startIndex = maxIndex;
438 // track is empty, clear selections
439 _currentPoint = _startIndex = _endIndex = -1;
441 UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);