]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/data/Selection.java
81ab64ce7c6cc42dc7df15d788e06a90df9bb31f
[GpsPrune.git] / tim / prune / data / Selection.java
1 package tim.prune.data;
2
3 import tim.prune.DataSubscriber;
4 import tim.prune.UpdateMessageBroker;
5
6 /**
7  * Class to represent a selected portion of a Track
8  * and its properties
9  */
10 public class Selection
11 {
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;
25
26
27         /**
28          * Constructor
29          * @param inTrack track object
30          */
31         public Selection(Track inTrack)
32         {
33                 _track = inTrack;
34         }
35
36
37         /**
38          * Mark selection invalid so it will be recalculated
39          */
40         public void markInvalid()
41         {
42                 _valid = false;
43         }
44
45
46         /**
47          * @return the current point index
48          */
49         public int getCurrentPointIndex()
50         {
51                 return _currentPoint;
52         }
53
54
55         /**
56          * @return true if range is selected
57          */
58         public boolean hasRangeSelected()
59         {
60                 return _startIndex >= 0 && _endIndex > _startIndex;
61         }
62
63
64         /**
65          * Recalculate all selection details
66          */
67         private void recalculate()
68         {
69                 _altitudeFormat = Altitude.Format.NO_FORMAT;
70                 _numSegments = 0;
71                 final int numPoints = _track.getNumPoints();
72                 // Recheck if the number of points has changed
73                 if (numPoints != _prevNumPoints) {
74                         _prevNumPoints = numPoints;
75                         check();
76                 }
77                 if (numPoints > 0 && hasRangeSelected())
78                 {
79                         _altitudeRange = new IntegerRange();
80                         _climb = 0;
81                         _descent = 0;
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;
88                         int altValue = 0;
89                         int lastAltValue = 0;
90                         boolean foundAlt = false;
91                         for (int i=_startIndex; i<=_endIndex; i++)
92                         {
93                                 currPoint = _track.getPoint(i);
94                                 altitude = currPoint.getAltitude();
95                                 // Ignore waypoints in altitude calculations
96                                 if (!currPoint.isWaypoint() && altitude.isValid())
97                                 {
98                                         altValue = altitude.getValue(_altitudeFormat);
99                                         if (_altitudeFormat == Altitude.Format.NO_FORMAT)
100                                                 _altitudeFormat = altitude.getFormat();
101                                         _altitudeRange.addValue(altValue);
102                                         if (foundAlt)
103                                         {
104                                                 if (altValue > lastAltValue)
105                                                         _climb += (altValue - lastAltValue);
106                                                 else
107                                                         _descent += (lastAltValue - altValue);
108                                         }
109                                         lastAltValue = altValue;
110                                         foundAlt = true;
111                                 }
112                                 // Store the first and last timestamp in the range
113                                 time = currPoint.getTimestamp();
114                                 if (time.isValid())
115                                 {
116                                         if (startTime == null || startTime.isAfter(time)) startTime = time;
117                                         if (endTime == null || time.isAfter(endTime)) endTime = time;
118                                         // add moving time
119                                         if (!currPoint.getSegmentStart() && previousTime != null && time.isAfter(previousTime)) {
120                                                 _movingSeconds += time.getSecondsSince(previousTime);
121                                         }
122                                         previousTime = time;
123                                 }
124                                 // Calculate distances, again ignoring waypoints
125                                 if (!currPoint.isWaypoint())
126                                 {
127                                         if (lastPoint != null)
128                                         {
129                                                 double radians = DataPoint.calculateRadiansBetween(lastPoint, currPoint);
130                                                 _angDistance += radians;
131                                                 if (currPoint.getSegmentStart()) {
132                                                         _numSegments++;
133                                                 }
134                                                 else {
135                                                         _angMovingDistance += radians;
136                                                 }
137                                         }
138                                         lastPoint = currPoint;
139                                         // If it's a track point then there must be at least one segment
140                                         if (_numSegments == 0) {_numSegments = 1;}
141                                 }
142                         }
143                         if (endTime != null) {
144                                 _totalSeconds = endTime.getSecondsSince(startTime);
145                         }
146                 }
147                 _valid = true;
148         }
149
150
151         /**
152          * @return start index
153          */
154         public int getStart()
155         {
156                 if (!_valid) recalculate();
157                 return _startIndex;
158         }
159
160
161         /**
162          * @return end index
163          */
164         public int getEnd()
165         {
166                 if (!_valid) recalculate();
167                 return _endIndex;
168         }
169
170
171         /**
172          * @return the altitude format, ie feet or metres
173          */
174         public Altitude.Format getAltitudeFormat()
175         {
176                 return _altitudeFormat;
177         }
178
179         /**
180          * @return altitude range
181          */
182         public IntegerRange getAltitudeRange()
183         {
184                 if (!_valid) recalculate();
185                 return _altitudeRange;
186         }
187
188
189         /**
190          * @return climb
191          */
192         public int getClimb()
193         {
194                 if (!_valid) recalculate();
195                 return _climb;
196         }
197
198         /**
199          * @return descent
200          */
201         public int getDescent()
202         {
203                 if (!_valid) recalculate();
204                 return _descent;
205         }
206
207
208         /**
209          * @return number of seconds spanned by selection
210          */
211         public long getNumSeconds()
212         {
213                 if (!_valid) recalculate();
214                 return _totalSeconds;
215         }
216
217         /**
218          * @return number of seconds spanned by segments within selection
219          */
220         public long getMovingSeconds()
221         {
222                 if (!_valid) recalculate();
223                 return _movingSeconds;
224         }
225
226         /**
227          * @param inUnits distance units to use, from class Distance
228          * @return distance of Selection in specified units
229          */
230         public double getDistance(Distance.Units inUnits)
231         {
232                 return Distance.convertRadiansToDistance(_angDistance, inUnits);
233         }
234
235         /**
236          * @param inUnits distance units to use, from class Distance
237          * @return moving distance of Selection in specified units
238          */
239         public double getMovingDistance(Distance.Units inUnits)
240         {
241                 return Distance.convertRadiansToDistance(_angMovingDistance, inUnits);
242         }
243
244         /**
245          * @return number of segments in selection
246          */
247         public int getNumSegments()
248         {
249                 return _numSegments;
250         }
251
252         /**
253          * Clear selected point, range, photo and audio
254          */
255         public void clearAll()
256         {
257                 _currentPoint = -1;
258                 selectRange(-1, -1);
259                 _currentPhotoIndex = -1;
260                 _currentAudioIndex = -1;
261                 check();
262         }
263
264
265         /**
266          * Select range from start to end
267          * @param inStartIndex index of start of range
268          * @param inEndIndex index of end of range
269          */
270         public void selectRange(int inStartIndex, int inEndIndex)
271         {
272                 _startIndex = inStartIndex;
273                 _endIndex = inEndIndex;
274                 markInvalid();
275                 check();
276         }
277
278
279         /**
280          * Select the range from the current point
281          */
282         public void selectRangeStart()
283         {
284                 selectRangeStart(_currentPoint);
285         }
286
287
288         /**
289          * Set the index for the start of the range selection
290          * @param inStartIndex start index
291          */
292         private void selectRangeStart(int inStartIndex)
293         {
294                 if (inStartIndex < 0)
295                 {
296                         _startIndex = _endIndex = -1;
297                 }
298                 else
299                 {
300                         _startIndex = inStartIndex;
301                         // Move end of selection to max if necessary
302                         if (_endIndex <= _startIndex)
303                         {
304                                 _endIndex = _track.getNumPoints() - 1;
305                         }
306                 }
307                 markInvalid();
308                 UpdateMessageBroker.informSubscribers();
309         }
310
311
312         /**
313          * Select the range up to the current point
314          */
315         public void selectRangeEnd()
316         {
317                 selectRangeEnd(_currentPoint);
318         }
319
320
321         /**
322          * Set the index for the end of the range selection
323          * @param inEndIndex end index
324          */
325         public void selectRangeEnd(int inEndIndex)
326         {
327                 if (inEndIndex < 0)
328                 {
329                         _startIndex = _endIndex = -1;
330                 }
331                 else
332                 {
333                         _endIndex = inEndIndex;
334                         // Move start of selection to min if necessary
335                         if (_startIndex > _endIndex || _startIndex < 0) {
336                                 _startIndex = 0;
337                         }
338                 }
339                 markInvalid();
340                 UpdateMessageBroker.informSubscribers();
341         }
342
343
344         /**
345          * Modify the selection given that the selected range has been deleted
346          */
347         public void modifyRangeDeleted()
348         {
349                 // Modify current point, if any
350                 if (_currentPoint > _endIndex)
351                 {
352                         _currentPoint -= (_endIndex - _startIndex);
353                 }
354                 else if (_currentPoint > _startIndex)
355                 {
356                         _currentPoint = _startIndex;
357                 }
358                 // Clear selected range
359                 _startIndex = _endIndex = -1;
360                 // Check for consistency and fire update
361                 check();
362         }
363
364
365         /**
366          * Modify the selection when a point is deleted
367          */
368         public void modifyPointDeleted()
369         {
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)
373                 {
374                         _endIndex--;
375                         if (_currentPoint < _startIndex)
376                                 _startIndex--;
377                         markInvalid();
378                 }
379                 check();
380         }
381
382
383         /**
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
388          */
389         public void selectPointPhotoAudio(int inPointIndex, int inPhotoIndex, int inAudioIndex)
390         {
391                 _currentPoint = inPointIndex;
392                 _currentPhotoIndex = inPhotoIndex;
393                 _currentAudioIndex = inAudioIndex;
394                 check();
395         }
396
397
398         /**
399          * @return currently selected photo index
400          */
401         public int getCurrentPhotoIndex()
402         {
403                 return _currentPhotoIndex;
404         }
405
406         /**
407          * @return currently selected audio index
408          */
409         public int getCurrentAudioIndex()
410         {
411                 return _currentAudioIndex;
412         }
413
414         /**
415          * Check that the selection still makes sense
416          * and fire update message to listeners
417          */
418         private void check()
419         {
420                 if (_track != null && _track.getNumPoints() > 0)
421                 {
422                         int maxIndex = _track.getNumPoints() - 1;
423                         if (_currentPoint > maxIndex)
424                         {
425                                 _currentPoint = maxIndex;
426                         }
427                         if (_endIndex > maxIndex)
428                         {
429                                 _endIndex = maxIndex;
430                         }
431                         if (_startIndex > maxIndex)
432                         {
433                                 _startIndex = maxIndex;
434                         }
435                 }
436                 else
437                 {
438                         // track is empty, clear selections
439                         _currentPoint = _startIndex = _endIndex = -1;
440                 }
441                 UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
442         }
443 }