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