1 package tim.prune.data;
4 * Class to do basic calculations of range statistics such as distances, durations,
5 * and altitude ranges, and to hold the results of the calculations.
7 public class RangeStats
9 private int _numPoints = 0;
10 private int _numSegments = 0;
11 private boolean _foundTrackPoint = false;
12 protected AltitudeRange _totalAltitudeRange = new AltitudeRange();
13 protected AltitudeRange _movingAltitudeRange = new AltitudeRange();
14 private Timestamp _earliestTimestamp = null, _latestTimestamp = null, _movingTimestamp = null;
15 private long _movingMilliseconds = 0L;
16 private boolean _timesIncomplete = false;
17 private boolean _timesOutOfSequence = false;
18 protected double _totalDistanceRads = 0.0, _movingDistanceRads = 0.0;
19 protected DataPoint _prevPoint = null;
27 * Constructor giving Track
28 * @param inTrack track object to calculate with
30 public RangeStats(Track inTrack, int inStartIndex, int inEndIndex)
32 populateFromTrack(inTrack, inStartIndex, inEndIndex);
36 * Add the specified points from the given track to the calculations
37 * @param inTrack track object
38 * @param inStartIndex start index (inclusive)
39 * @param inEndIndex end index (inclusive)
41 protected void populateFromTrack(Track inTrack, int inStartIndex, int inEndIndex)
43 for (int i=inStartIndex; i<=inEndIndex; i++)
45 addPoint(inTrack.getPoint(i));
50 * @param inPoint point to add to the calculations
52 public void addPoint(DataPoint inPoint)
59 // ignore all waypoints
60 if (inPoint.isWaypoint()) {
63 if (inPoint.getSegmentStart() || !_foundTrackPoint) {
66 _foundTrackPoint = true;
67 // Get the distance to the previous track point
68 if (_prevPoint != null)
70 double rads = DataPoint.calculateRadiansBetween(_prevPoint, inPoint);
71 _totalDistanceRads += rads;
72 if (!inPoint.getSegmentStart()) {
73 _movingDistanceRads += rads;
78 if (inPoint.getSegmentStart())
80 // reset movingTimestamp for moving time at the start
82 _movingTimestamp = null;
84 if (inPoint.hasTimestamp())
86 Timestamp currTstamp = inPoint.getTimestamp();
87 if (_earliestTimestamp == null || currTstamp.isBefore(_earliestTimestamp)) {
88 _earliestTimestamp = currTstamp;
90 if (_latestTimestamp == null || currTstamp.isAfter(_latestTimestamp)) {
91 _latestTimestamp = currTstamp;
94 // Work out duration without segment gaps
95 if (_movingTimestamp != null)
97 long millisLater = currTstamp.getMillisecondsSince(_movingTimestamp);
98 if (millisLater < 0) {
99 _timesOutOfSequence = true;
102 _movingMilliseconds += millisLater;
105 _movingTimestamp = currTstamp;
108 _timesIncomplete = true;
112 if (inPoint.hasAltitude())
114 Altitude altitude = inPoint.getAltitude();
115 _totalAltitudeRange.addValue(altitude);
116 if (inPoint.getSegmentStart()) {
117 _movingAltitudeRange.ignoreValue(altitude);
121 _movingAltitudeRange.addValue(altitude);
125 // allow child classes to do additional calculations
126 doFurtherCalculations(inPoint);
128 _prevPoint = inPoint;
132 * Hook for subclasses to do what they want in addition
133 * @param inPoint incoming point
135 protected void doFurtherCalculations(DataPoint inPoint)
140 /** @return number of points in range */
141 public int getNumPoints() {
145 /** @return number of segments in range */
146 public int getNumSegments() {
150 /** @return altitude range of range including segment gaps */
151 public AltitudeRange getTotalAltitudeRange() {
152 return _totalAltitudeRange;
155 /** @return altitude range of range just within segments */
156 public AltitudeRange getMovingAltitudeRange() {
157 return _movingAltitudeRange;
160 /** @return the earliest timestamp found */
161 public Timestamp getEarliestTimestamp() {
162 return _earliestTimestamp;
165 /** @return the latest timestamp found */
166 public Timestamp getLatestTimestamp() {
167 return _latestTimestamp;
170 /** @return total number of seconds in the range */
171 public long getTotalDurationInSeconds()
173 if (_earliestTimestamp != null && _latestTimestamp != null) {
174 return _latestTimestamp.getSecondsSince(_earliestTimestamp);
179 /** @return number of seconds within the segments of the range */
180 public long getMovingDurationInSeconds()
182 return _movingMilliseconds / 1000;
185 /** @return true if any timestamps are missing */
186 public boolean getTimestampsIncomplete() {
187 return _timesIncomplete;
190 /** @return true if any timestamps are out of sequence */
191 public boolean getTimestampsOutOfSequence() {
192 return _timesOutOfSequence;
195 /** @return total distance in the current distance units (km or mi) */
196 public double getTotalDistance() {
197 return Distance.convertRadiansToDistance(_totalDistanceRads);
200 /** @return moving distance in the current distance units (km or mi) */
201 public double getMovingDistance() {
202 return Distance.convertRadiansToDistance(_movingDistanceRads);
205 /** @return moving distance in km */
206 public double getMovingDistanceKilometres() {
207 return Distance.convertRadiansToDistance(_movingDistanceRads, UnitSetLibrary.UNITS_KILOMETRES);
211 * @return the total vertical speed (including segment gaps) in metric units
213 public double getTotalVerticalSpeed()
215 long time = getTotalDurationInSeconds();
216 if (time > 0 && _totalAltitudeRange.hasRange()) {
217 return _totalAltitudeRange.getMetricHeightDiff() / time;
223 * @return the moving vertical speed (ignoring segment gaps) in metric units
225 public double getMovingVerticalSpeed()
227 long time = getMovingDurationInSeconds();
228 if (time > 0 && _movingAltitudeRange.hasRange()) {
229 return _movingAltitudeRange.getMetricHeightDiff() / time;