package tim.prune.data; import tim.prune.UpdateMessageBroker; /** * Class to represent a selected portion of a Track * and its properties */ public class Selection { private Track _track = null; private UpdateMessageBroker _broker = null; private int _currentPoint = -1; private boolean _valid = false; private int _startIndex = -1, _endIndex = -1; private IntegerRange _altitudeRange = null; private int _climb = -1, _descent = -1; private int _altitudeFormat = Altitude.FORMAT_NONE; private long _seconds = 0L; private double _angDistance = -1.0; //, _averageSpeed = -1.0; /** * Constructor * @param inTrack track object * @param inBroker broker object */ public Selection(Track inTrack, UpdateMessageBroker inBroker) { _track = inTrack; _broker = inBroker; } /** * Reset selection to be recalculated */ private void reset() { _valid = false; } /** * Select the point at the given index * @param inIndex index number of selected point */ public void selectPoint(int inIndex) { if (inIndex >= -1) { _currentPoint = inIndex; check(); } } /** * Select the specified point and range in one go * @param inPointIndex point selection * @param inStart range start * @param inEnd range end */ public void select(int inPointIndex, int inStart, int inEnd) { _currentPoint = inPointIndex; _startIndex = inStart; _endIndex = inEnd; reset(); check(); } /** * Select the previous point */ public void selectPreviousPoint() { if (_currentPoint > 0) selectPoint(_currentPoint - 1); } /** * Select the next point */ public void selectNextPoint() { selectPoint(_currentPoint + 1); } /** * @return the current point index */ public int getCurrentPointIndex() { return _currentPoint; } /** * @return true if range is selected */ public boolean hasRangeSelected() { return _startIndex >= 0 && _endIndex > _startIndex; } /** * Recalculate all selection details */ private void recalculate() { _altitudeFormat = Altitude.FORMAT_NONE; if (_track.getNumPoints() > 0 && hasRangeSelected()) { _altitudeRange = new IntegerRange(); _climb = 0; _descent = 0; Altitude altitude = null; Timestamp time = null, startTime = null, endTime = null; DataPoint lastPoint = null, currPoint = null; _angDistance = 0.0; int altValue = 0; int lastAltValue = 0; boolean foundAlt = false; for (int i=_startIndex; i<=_endIndex; i++) { currPoint = _track.getPoint(i); altitude = currPoint.getAltitude(); // Ignore waypoints in altitude calculations if (!currPoint.isWaypoint() && altitude.isValid()) { altValue = altitude.getValue(_altitudeFormat); if (_altitudeFormat == Altitude.FORMAT_NONE) _altitudeFormat = altitude.getFormat(); _altitudeRange.addValue(altValue); if (foundAlt) { if (altValue > lastAltValue) _climb += (altValue - lastAltValue); else _descent += (lastAltValue - altValue); } lastAltValue = altValue; foundAlt = true; } // Store the first and last timestamp in the range time = currPoint.getTimestamp(); if (time.isValid()) { if (startTime == null) startTime = time; endTime = time; } // Calculate distances, again ignoring waypoints if (!currPoint.isWaypoint()) { if (lastPoint != null) { _angDistance += DataPoint.calculateRadiansBetween(lastPoint, currPoint); } lastPoint = currPoint; } } if (endTime != null) { _seconds = endTime.getSecondsSince(startTime); } else { _seconds = 0L; } } _valid = true; } /** * @return start index */ public int getStart() { if (!_valid) recalculate(); return _startIndex; } /** * @return end index */ public int getEnd() { if (!_valid) recalculate(); return _endIndex; } /** * @return the altitude format, ie feet or metres */ public int getAltitudeFormat() { return _altitudeFormat; } /** * @return altitude range */ public IntegerRange getAltitudeRange() { if (!_valid) recalculate(); return _altitudeRange; } /** * @return climb */ public int getClimb() { if (!_valid) recalculate(); return _climb; } /** * @return descent */ public int getDescent() { if (!_valid) recalculate(); return _descent; } /** * @return number of seconds spanned by selection */ public long getNumSeconds() { if (!_valid) recalculate(); return _seconds; } /** * @param inFormat distance units to use, from class Distance * @return distance of Selection in specified units */ public double getDistance(int inUnits) { return Distance.convertRadians(_angDistance, inUnits); } /** * Clear selected point and range */ public void clearAll() { _currentPoint = -1; deselectRange(); } /** * Deselect range */ public void deselectRange() { _startIndex = _endIndex = -1; reset(); check(); } /** * Select the range from the current point */ public void selectRangeStart() { selectRangeStart(_currentPoint); } /** * Set the index for the start of the range selection * @param inStartIndex start index */ public void selectRangeStart(int inStartIndex) { if (inStartIndex < 0) { _startIndex = _endIndex = -1; } else { _startIndex = inStartIndex; // Move end of selection to max if necessary if (_endIndex <= _startIndex) { _endIndex = _track.getNumPoints() - 1; } } reset(); _broker.informSubscribers(); } /** * Select the range up to the current point */ public void selectRangeEnd() { selectRangeEnd(_currentPoint); } /** * Set the index for the end of the range selection * @param inEndIndex end index */ public void selectRangeEnd(int inEndIndex) { if (inEndIndex < 0) { _startIndex = _endIndex = -1; } else { _endIndex = inEndIndex; // Move start of selection to min if necessary if (_startIndex > _endIndex || _startIndex < 0) { _startIndex = 0; } } reset(); _broker.informSubscribers(); } /** * Modify the selection given that the selected range has been deleted */ public void modifyRangeDeleted() { // Modify current point, if any if (_currentPoint > _endIndex) { _currentPoint -= (_endIndex - _startIndex); } else if (_currentPoint > _startIndex) { _currentPoint = _startIndex; } // Clear selected range _startIndex = _endIndex = -1; // Check for consistency and fire update check(); } /** * Modify the selection when a point is deleted */ public void modifyPointDeleted() { // current point index doesn't change, just gets checked // range needs to get altered if deleted point is inside or before if (hasRangeSelected() && _currentPoint <= _endIndex) { _endIndex--; if (_currentPoint < _startIndex) _startIndex--; reset(); } check(); } /** * Check that the selection still makes sense * and fire update message to listeners */ private void check() { if (_track != null && _track.getNumPoints() > 0) { int maxIndex = _track.getNumPoints() - 1; if (_currentPoint > maxIndex) { _currentPoint = maxIndex; } if (_endIndex > maxIndex) { _endIndex = maxIndex; } if (_startIndex > maxIndex) { _startIndex = maxIndex; } } _broker.informSubscribers(); } }