]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/function/FullRangeDetails.java
Version 14, October 2012
[GpsPrune.git] / tim / prune / function / FullRangeDetails.java
1 package tim.prune.function;
2
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.FlowLayout;
6 import java.awt.GridLayout;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.awt.event.KeyAdapter;
10 import java.awt.event.KeyEvent;
11 import java.text.NumberFormat;
12
13 import javax.swing.BorderFactory;
14 import javax.swing.JButton;
15 import javax.swing.JDialog;
16 import javax.swing.JLabel;
17 import javax.swing.JPanel;
18
19 import tim.prune.App;
20 import tim.prune.GenericFunction;
21 import tim.prune.I18nManager;
22 import tim.prune.config.Config;
23 import tim.prune.data.Altitude;
24 import tim.prune.data.AltitudeRange;
25 import tim.prune.data.DataPoint;
26 import tim.prune.data.Selection;
27 import tim.prune.data.Unit;
28 import tim.prune.gui.DisplayUtils;
29 import tim.prune.gui.profile.SpeedData;
30
31 /**
32  * Class to show the full range details in a separate popup
33  */
34 public class FullRangeDetails extends GenericFunction
35 {
36         /** Dialog */
37         private JDialog _dialog = null;
38         /** Label for number of points */
39         private JLabel _numPointsLabel = null;
40         /** Label for number of segments */
41         private JLabel _numSegsLabel = null;
42         /** Label for the maximum speed */
43         private JLabel _maxSpeedLabel = null;
44
45         /** Label for heading of "total" column */
46         private JLabel _colTotalLabel = null;
47         /** Label for heading of "segments" column */
48         private JLabel _colSegmentsLabel = null;
49         /** Labels for distances */
50         private JLabel _totalDistanceLabel = null, _movingDistanceLabel = null;
51         /** Labels for durations */
52         private JLabel _totalDurationLabel = null, _movingDurationLabel = null;
53         /** Labels for climbs */
54         private JLabel _totalClimbLabel = null, _movingClimbLabel = null;
55         /** Labels for descents */
56         private JLabel _totalDescentLabel = null, _movingDescentLabel = null;
57         /** Labels for pace */
58         private JLabel _totalPaceLabel = null, _movingPaceLabel = null;
59         /** Labels for gradient */
60         private JLabel _totalGradientLabel = null, _movingGradientLabel = null;
61         /** Labels for speed */
62         private JLabel _totalSpeedLabel, _movingSpeedLabel = null;
63         /** Labels for vertical speed */
64         private JLabel _totalVertSpeedLabel, _movingVertSpeedLabel = null;
65
66         /** Number formatter for one decimal place */
67         private static final NumberFormat FORMAT_ONE_DP = NumberFormat.getNumberInstance();
68         /** Flexible number formatter for different decimal places */
69         private NumberFormat _distanceFormatter = NumberFormat.getInstance();
70
71         /**
72          * Constructor
73          * @param inApp App object
74          */
75         public FullRangeDetails(App inApp)
76         {
77                 super(inApp);
78                 FORMAT_ONE_DP.setMaximumFractionDigits(1);
79                 FORMAT_ONE_DP.setMinimumFractionDigits(1);
80         }
81
82         /** Get the name key */
83         public String getNameKey() {
84                 return "function.fullrangedetails";
85         }
86
87         /**
88          * Begin the function
89          */
90         public void begin()
91         {
92                 if (_dialog == null)
93                 {
94                         _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
95                         _dialog.setLocationRelativeTo(_parentFrame);
96                         _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
97                         _dialog.getContentPane().add(makeDialogComponents());
98                         _dialog.pack();
99                 }
100                 updateDetails();
101                 _dialog.setVisible(true);
102         }
103
104         /**
105          * Create dialog components
106          * @return Panel containing all gui elements in dialog
107          */
108         private Component makeDialogComponents()
109         {
110                 JPanel dialogPanel = new JPanel();
111                 dialogPanel.setLayout(new BorderLayout(5, 5));
112                 // Label at top
113                 JLabel topLabel = new JLabel(I18nManager.getText("dialog.fullrangedetails.intro") + ":");
114                 topLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
115                 dialogPanel.add(topLabel, BorderLayout.NORTH);
116
117                 // Details panel in middle
118                 JPanel midPanel = new JPanel();
119                 midPanel.setLayout(new GridLayout(0, 3, 6, 2));
120                 midPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
121                 // Number of points
122                 JLabel pointsLabel = new JLabel(I18nManager.getText("details.track.points") + ": ");
123                 pointsLabel.setHorizontalAlignment(JLabel.RIGHT);
124                 midPanel.add(pointsLabel);
125                 _numPointsLabel = new JLabel("100");
126                 midPanel.add(_numPointsLabel);
127                 midPanel.add(new JLabel(" "));
128                 // Number of segments
129                 JLabel segLabel = new JLabel(I18nManager.getText("details.range.numsegments") + ": ");
130                 segLabel.setHorizontalAlignment(JLabel.RIGHT);
131                 midPanel.add(segLabel);
132                 _numSegsLabel = new JLabel("100");
133                 midPanel.add(_numSegsLabel);
134                 midPanel.add(new JLabel(" "));
135                 // Maximum speed
136                 JLabel maxSpeedLabel = new JLabel(I18nManager.getText("details.range.maxspeed") + ": ");
137                 maxSpeedLabel.setHorizontalAlignment(JLabel.RIGHT);
138                 midPanel.add(maxSpeedLabel);
139                 _maxSpeedLabel = new JLabel("10 km/h");
140                 midPanel.add(_maxSpeedLabel);
141                 midPanel.add(new JLabel(" "));
142
143                 // blank row
144                 for (int i=0; i<3; i++) midPanel.add(new JLabel(" "));
145
146                 // Row for column headings
147                 midPanel.add(new JLabel(" "));
148                 _colTotalLabel = new JLabel(I18nManager.getText("dialog.fullrangedetails.coltotal"));
149                 midPanel.add(_colTotalLabel);
150                 _colSegmentsLabel = new JLabel(I18nManager.getText("dialog.fullrangedetails.colsegments"));
151                 midPanel.add(_colSegmentsLabel);
152
153                 // Distance
154                 JLabel distLabel = new JLabel(I18nManager.getText("fieldname.distance") + ": ");
155                 distLabel.setHorizontalAlignment(JLabel.RIGHT);
156                 midPanel.add(distLabel);
157                 _totalDistanceLabel = new JLabel("5 km");
158                 midPanel.add(_totalDistanceLabel);
159                 _movingDistanceLabel = new JLabel("5 km");
160                 midPanel.add(_movingDistanceLabel);
161
162                 // Duration
163                 JLabel durationLabel = new JLabel(I18nManager.getText("fieldname.duration") + ": ");
164                 durationLabel.setHorizontalAlignment(JLabel.RIGHT);
165                 midPanel.add(durationLabel);
166                 _totalDurationLabel = new JLabel("15 min");
167                 midPanel.add(_totalDurationLabel);
168                 _movingDurationLabel = new JLabel("15 min");
169                 midPanel.add(_movingDurationLabel);
170
171                 // Speed
172                 JLabel speedLabel = new JLabel(I18nManager.getText("details.range.avespeed") + ": ");
173                 speedLabel.setHorizontalAlignment(JLabel.RIGHT);
174                 midPanel.add(speedLabel);
175                 _totalSpeedLabel = new JLabel("5.5 km/h");
176                 midPanel.add(_totalSpeedLabel);
177                 _movingSpeedLabel = new JLabel("5.5 km/h");
178                 midPanel.add(_movingSpeedLabel);
179
180                 // Pace
181                 JLabel paceLabel = new JLabel(I18nManager.getText("details.range.pace") + ": ");
182                 paceLabel.setHorizontalAlignment(JLabel.RIGHT);
183                 midPanel.add(paceLabel);
184                 _totalPaceLabel = new JLabel("8 min/km");
185                 midPanel.add(_totalPaceLabel);
186                 _movingPaceLabel = new JLabel("8 min/km");
187                 midPanel.add(_movingPaceLabel);
188
189                 // Climb
190                 JLabel climbLabel = new JLabel(I18nManager.getText("details.range.climb") + ": ");
191                 climbLabel.setHorizontalAlignment(JLabel.RIGHT);
192                 midPanel.add(climbLabel);
193                 _totalClimbLabel = new JLabel("1000 m");
194                 midPanel.add(_totalClimbLabel);
195                 _movingClimbLabel = new JLabel("1000 m");
196                 midPanel.add(_movingClimbLabel);
197                 // Descent
198                 JLabel descentLabel = new JLabel(I18nManager.getText("details.range.descent") + ": ");
199                 descentLabel.setHorizontalAlignment(JLabel.RIGHT);
200                 midPanel.add(descentLabel);
201                 _totalDescentLabel = new JLabel("1000 m");
202                 midPanel.add(_totalDescentLabel);
203                 _movingDescentLabel = new JLabel("1000 m");
204                 midPanel.add(_movingDescentLabel);
205
206                 // Gradient
207                 JLabel gradientLabel = new JLabel(I18nManager.getText("details.range.gradient") + ": ");
208                 gradientLabel.setHorizontalAlignment(JLabel.RIGHT);
209                 midPanel.add(gradientLabel);
210                 _totalGradientLabel = new JLabel("10 %");
211                 midPanel.add(_totalGradientLabel);
212                 _movingGradientLabel = new JLabel("10 %");
213                 midPanel.add(_movingGradientLabel);
214
215                 // Vertical speed
216                 JLabel vSpeedLabel = new JLabel(I18nManager.getText("fieldname.verticalspeed") + ": ");
217                 vSpeedLabel.setHorizontalAlignment(JLabel.RIGHT);
218                 midPanel.add(vSpeedLabel);
219                 _totalVertSpeedLabel = new JLabel("1 m/s");
220                 midPanel.add(_totalVertSpeedLabel);
221                 _movingVertSpeedLabel = new JLabel("1 m/s");
222                 midPanel.add(_movingVertSpeedLabel);
223
224                 dialogPanel.add(midPanel, BorderLayout.CENTER);
225                 // button panel at bottom
226                 JPanel buttonPanel = new JPanel();
227                 buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
228                 JButton closeButton = new JButton(I18nManager.getText("button.close"));
229                 closeButton.addActionListener(new ActionListener() {
230                         public void actionPerformed(ActionEvent e)
231                         {
232                                 _dialog.dispose();
233                         }
234                 });
235                 closeButton.addKeyListener(new KeyAdapter() {
236                         public void keyPressed(KeyEvent inE) {
237                                 if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
238                                 super.keyPressed(inE);
239                         }
240                 });
241                 buttonPanel.add(closeButton);
242                 dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
243                 return dialogPanel;
244         }
245
246
247         /**
248          * Update the labels with the current details
249          */
250         private void updateDetails()
251         {
252                 Selection selection = _app.getTrackInfo().getSelection();
253                 // Number of points
254                 _numPointsLabel.setText("" + (selection.getEnd()-selection.getStart()+1));
255                 // Number of segments
256                 _numSegsLabel.setText("" + selection.getNumSegments());
257                 final boolean isMultiSegments = (selection.getNumSegments() > 1);
258                 // Set visibility of third column accordingly
259                 _movingDistanceLabel.setVisible(isMultiSegments);
260                 _movingDurationLabel.setVisible(isMultiSegments);
261                 _movingClimbLabel.setVisible(isMultiSegments);
262                 _movingDescentLabel.setVisible(isMultiSegments);
263                 _movingSpeedLabel.setVisible(isMultiSegments);
264                 _movingPaceLabel.setVisible(isMultiSegments);
265                 _movingGradientLabel.setVisible(isMultiSegments);
266                 _movingVertSpeedLabel.setVisible(isMultiSegments);
267
268                 // Distance in current units
269                 final Unit distUnit = Config.getUnitSet().getDistanceUnit();
270                 final String distUnitsStr = I18nManager.getText(distUnit.getShortnameKey());
271                 final double selectionDistance = selection.getDistance();
272                 _totalDistanceLabel.setText(roundedNumber(selectionDistance) + " " + distUnitsStr);
273
274                 // Duration
275                 long numSecs = selection.getNumSeconds();
276                 _totalDurationLabel.setText(DisplayUtils.buildDurationString(numSecs));
277                 // Climb and descent
278                 final Unit altUnit = Config.getUnitSet().getAltitudeUnit();
279                 final String altUnitsStr = " " + I18nManager.getText(altUnit.getShortnameKey());
280                 if (selection.getAltitudeRange().hasRange()) {
281                         _totalClimbLabel.setText(selection.getAltitudeRange().getClimb(altUnit) + altUnitsStr);
282                         _totalDescentLabel.setText(selection.getAltitudeRange().getDescent(altUnit) + altUnitsStr);
283                 }
284                 else {
285                         _totalClimbLabel.setText("");
286                         _totalDescentLabel.setText("");
287                 }
288
289                 // Overall pace and speed
290                 final String speedUnitsStr = I18nManager.getText(Config.getUnitSet().getSpeedUnit().getShortnameKey());
291                 if (numSecs > 0 && selectionDistance > 0)
292                 {
293                         _totalPaceLabel.setText(
294                                 DisplayUtils.buildDurationString((long) (numSecs/selectionDistance))
295                                 + " / " + distUnitsStr);
296                         _totalSpeedLabel.setText(roundedNumber(selectionDistance/numSecs*3600.0)
297                                 + " " + speedUnitsStr);
298                 }
299                 else {
300                         _totalPaceLabel.setText("");
301                         _totalSpeedLabel.setText("");
302                 }
303
304                 // Moving distance
305                 double movingDist = selection.getMovingDistance();
306                 _movingDistanceLabel.setText(roundedNumber(movingDist) + " " + distUnitsStr);
307                 // Moving average speed
308                 long numMovingSecs = selection.getMovingSeconds();
309                 if (numMovingSecs > 0)
310                 {
311                         _movingDurationLabel.setText(DisplayUtils.buildDurationString(numMovingSecs));
312                         _movingSpeedLabel.setText(roundedNumber(movingDist/numMovingSecs*3600.0)
313                                 + " " + speedUnitsStr);
314                         _movingPaceLabel.setText(
315                                 DisplayUtils.buildDurationString((long) (numMovingSecs/movingDist))
316                                 + " / " + distUnitsStr);
317                 }
318                 else
319                 {
320                         _movingDurationLabel.setText("");
321                         _movingSpeedLabel.setText("");
322                         _movingPaceLabel.setText("");
323                 }
324
325                 // Moving gradient and moving climb/descent
326                 Altitude firstAlt = null, lastAlt = null;
327                 Altitude veryFirstAlt = null, veryLastAlt = null;
328                 AltitudeRange altRange = new AltitudeRange();
329                 double movingHeightDiff = 0.0;
330                 if (movingDist > 0.0)
331                 {
332                         for (int pNum = selection.getStart(); pNum <= selection.getEnd(); pNum++)
333                         {
334                                 DataPoint p = _app.getTrackInfo().getTrack().getPoint(pNum);
335                                 if (p != null && !p.isWaypoint())
336                                 {
337                                         // If we're starting a new segment, calculate the height diff of the previous one
338                                         if (p.getSegmentStart())
339                                         {
340                                                 if (firstAlt != null && firstAlt.isValid() && lastAlt != null && lastAlt.isValid())
341                                                         movingHeightDiff = movingHeightDiff + lastAlt.getMetricValue() - firstAlt.getMetricValue();
342                                                 firstAlt = null; lastAlt = null;
343                                         }
344                                         Altitude alt = p.getAltitude();
345                                         if (alt != null && alt.isValid())
346                                         {
347                                                 if (firstAlt == null) firstAlt = alt;
348                                                 else lastAlt = alt;
349                                                 if (veryFirstAlt == null) veryFirstAlt = alt;
350                                                 else veryLastAlt = alt;
351                                         }
352                                         // Keep track of climb and descent too
353                                         if (p.getSegmentStart())
354                                                 altRange.ignoreValue(alt);
355                                         else
356                                                 altRange.addValue(alt);
357                                 }
358                         }
359                         // deal with last segment
360                         if (firstAlt != null && firstAlt.isValid() && lastAlt != null && lastAlt.isValid())
361                                 movingHeightDiff = movingHeightDiff + lastAlt.getMetricValue() - firstAlt.getMetricValue();
362                         final double metricMovingDist = movingDist / distUnit.getMultFactorFromStd(); // convert back to metres
363                         final double gradient = movingHeightDiff * 100.0 / metricMovingDist;
364                         _movingGradientLabel.setText(FORMAT_ONE_DP.format(gradient) + " %");
365                 }
366                 if (!altRange.hasRange()) {
367                         _movingGradientLabel.setText("");
368                 }
369                 final boolean hasAltitudes = veryFirstAlt != null && veryFirstAlt.isValid() && veryLastAlt != null && veryLastAlt.isValid();
370
371                 // Total gradient
372                 final double metreDist = selection.getDistance() / distUnit.getMultFactorFromStd(); // convert back to metres
373                 if (hasAltitudes && metreDist > 0.0)
374                 {
375                         // got an altitude and range
376                         int altDiffInMetres = veryLastAlt.getValue(Altitude.Format.METRES) - veryFirstAlt.getValue(Altitude.Format.METRES);
377                         double gradient = altDiffInMetres * 100.0 / metreDist;
378                         _totalGradientLabel.setText(FORMAT_ONE_DP.format(gradient) + " %");
379                 }
380                 else {
381                         // no altitude given
382                         _totalGradientLabel.setText("");
383                 }
384
385                 // Moving climb/descent
386                 if (altRange.hasRange()) {
387                         _movingClimbLabel.setText(altRange.getClimb(altUnit) + altUnitsStr);
388                         _movingDescentLabel.setText(altRange.getDescent(altUnit) + altUnitsStr);
389                 }
390                 else {
391                         _movingClimbLabel.setText("");
392                         _movingDescentLabel.setText("");
393                 }
394                 // Maximum speed
395                 SpeedData speeds = new SpeedData(_app.getTrackInfo().getTrack());
396                 speeds.init(Config.getUnitSet());
397                 double maxSpeed = 0.0;
398                 for (int i=selection.getStart(); i<=selection.getEnd(); i++)
399                 {
400                         if (speeds.hasData(i) && (speeds.getData(i) > maxSpeed)) {
401                                 maxSpeed = speeds.getData(i);
402                         }
403                 }
404                 if (maxSpeed > 0.0) {
405                         _maxSpeedLabel.setText(roundedNumber(maxSpeed) + " " + speedUnitsStr);
406                 }
407                 else {
408                         _maxSpeedLabel.setText("");
409                 }
410
411                 // vertical speed
412                 final String vertSpeedUnitsStr = I18nManager.getText(Config.getUnitSet().getVerticalSpeedUnit().getShortnameKey());
413                 if (hasAltitudes && metreDist > 0.0 && numSecs > 0)
414                 {
415                         // got an altitude and time - do total
416                         final int altDiffInMetres = veryLastAlt.getValue(Altitude.Format.METRES) - veryFirstAlt.getValue(Altitude.Format.METRES);
417                         final double altDiff = altDiffInMetres * altUnit.getMultFactorFromStd();
418                         _totalVertSpeedLabel.setText(roundedNumber(altDiff/numSecs) + " " + vertSpeedUnitsStr);
419                         // and moving
420                         _movingVertSpeedLabel.setText(roundedNumber(movingHeightDiff * altUnit.getMultFactorFromStd() / numMovingSecs) + " " + vertSpeedUnitsStr);
421                 }
422                 else {
423                         // no vertical speed available
424                         _totalVertSpeedLabel.setText("");
425                         _movingVertSpeedLabel.setText("");
426                 }
427         }
428
429         /**
430          * Format a number to a sensible precision
431          * @param inDist distance
432          * @return formatted String
433          */
434         private String roundedNumber(double inDist)
435         {
436                 // Set precision of formatter
437                 int numDigits = 0;
438                 if (inDist < 1.0)
439                         numDigits = 3;
440                 else if (inDist < 10.0)
441                         numDigits = 2;
442                 else if (inDist < 100.0)
443                         numDigits = 1;
444                 // set formatter
445                 _distanceFormatter.setMaximumFractionDigits(numDigits);
446                 _distanceFormatter.setMinimumFractionDigits(numDigits);
447                 return _distanceFormatter.format(inDist);
448         }
449 }