]> gitweb.fperrin.net Git - GpsPrune.git/blob - src/tim/prune/function/ShowFullDetails.java
Version 20.4, May 2021
[GpsPrune.git] / src / tim / prune / function / ShowFullDetails.java
1 package tim.prune.function;
2
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.Dimension;
6 import java.awt.FlowLayout;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.awt.event.KeyEvent;
10 import java.awt.event.KeyListener;
11 import java.util.TimeZone;
12
13 import javax.swing.JButton;
14 import javax.swing.JDialog;
15 import javax.swing.JPanel;
16 import javax.swing.JScrollPane;
17 import javax.swing.JTabbedPane;
18 import javax.swing.JTextArea;
19
20 import tim.prune.App;
21 import tim.prune.GenericFunction;
22 import tim.prune.I18nManager;
23 import tim.prune.config.Config;
24 import tim.prune.config.TimezoneHelper;
25 import tim.prune.data.AltitudeRange;
26 import tim.prune.data.AudioClip;
27 import tim.prune.data.Coordinate;
28 import tim.prune.data.DataPoint;
29 import tim.prune.data.Field;
30 import tim.prune.data.Photo;
31 import tim.prune.data.RangeStatsWithGradients;
32 import tim.prune.data.Selection;
33 import tim.prune.data.SpeedCalculator;
34 import tim.prune.data.SpeedValue;
35 import tim.prune.data.Track;
36 import tim.prune.data.Unit;
37 import tim.prune.data.UnitSet;
38 import tim.prune.gui.CoordDisplay;
39 import tim.prune.gui.DisplayUtils;
40
41
42 /**
43  * Class to show the full point/range details in a separate popup
44  */
45 public class ShowFullDetails extends GenericFunction
46 {
47         private JDialog _dialog = null;
48         private JTabbedPane _tabs = null;
49         private JButton _okButton = null;
50
51         private JTextArea _pointTextArea = null;
52         private JTextArea _rangeTextArea = null;
53
54
55         /**
56          * Constructor
57          * @param inApp App object
58          */
59         public ShowFullDetails(App inApp)
60         {
61                 super(inApp);
62         }
63
64         /** Get the name key */
65         public String getNameKey() {
66                 return "function.viewfulldetails";
67         }
68
69         /**
70          * Begin the function
71          */
72         public void begin()
73         {
74                 if (_dialog == null)
75                 {
76                         _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
77                         _dialog.setLocationRelativeTo(_parentFrame);
78                         _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
79                         _dialog.getContentPane().add(makeDialogComponents());
80                         _dialog.pack();
81                 }
82                 updateDetails();
83                 _dialog.setVisible(true);
84                 _okButton.requestFocus();
85         }
86
87         /**
88          * Create dialog components
89          * @return Panel containing all gui elements in dialog
90          */
91         private Component makeDialogComponents()
92         {
93                 JPanel mainPanel = new JPanel();
94                 mainPanel.setLayout(new BorderLayout());
95
96                 _tabs = new JTabbedPane();
97                 mainPanel.add(_tabs, BorderLayout.CENTER);
98
99                 JPanel pointPanel = new JPanel();
100                 pointPanel.setLayout(new BorderLayout());
101                 _pointTextArea = new JTextArea(I18nManager.getText("details.nopointselection"));
102                 _pointTextArea.setEditable(false);
103                 _pointTextArea.setLineWrap(true);
104                 JScrollPane scrollPane = new JScrollPane(_pointTextArea);
105                 scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
106                 scrollPane.setPreferredSize(new Dimension(500, 230));
107                 pointPanel.add(scrollPane, BorderLayout.CENTER);
108                 _tabs.add(I18nManager.getText("details.pointdetails"), pointPanel);
109
110                 JPanel rangePanel = new JPanel();
111                 rangePanel.setLayout(new BorderLayout());
112                 _rangeTextArea = new JTextArea(I18nManager.getText("details.norangeselection"));
113                 _rangeTextArea.setEditable(false);
114                 _rangeTextArea.setLineWrap(true);
115                 JScrollPane scrollPane2 = new JScrollPane(_rangeTextArea);
116                 scrollPane2.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
117                 scrollPane2.setPreferredSize(new Dimension(500, 230));
118                 rangePanel.add(scrollPane2, BorderLayout.CENTER);
119                 _tabs.add(I18nManager.getText("details.rangedetails"), rangePanel);
120
121                 // OK button at the bottom
122                 JPanel okPanel = new JPanel();
123                 okPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
124                 _okButton = new JButton(I18nManager.getText("button.ok"));
125                 _okButton.addActionListener(new ActionListener()
126                 {
127                         public void actionPerformed(ActionEvent e)
128                         {
129                                 _dialog.dispose();
130                         }
131                 });
132                 _okButton.addKeyListener(new KeyListener() {
133                         public void keyPressed(KeyEvent e)
134                         {
135                                 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
136                         }
137                         public void keyTyped(KeyEvent e) {}
138                         public void keyReleased(KeyEvent e) {}
139                 });
140                 okPanel.add(_okButton);
141                 mainPanel.add(okPanel, BorderLayout.SOUTH);
142                 return mainPanel;
143         }
144
145
146         /**
147          * Update the labels with the current details
148          */
149         private void updateDetails()
150         {
151                 if (_app.getTrackInfo().getCurrentPoint() != null)
152                 {
153                         final String pointString = makePointDescription(_app.getTrackInfo().getTrack(),
154                                 _app.getTrackInfo().getSelection().getCurrentPointIndex());
155                         _pointTextArea.setText(pointString);
156                         // Select point tab
157                         _tabs.setSelectedIndex(0);
158                 }
159                 else
160                 {
161                         _pointTextArea.setText(I18nManager.getText("details.nopointselection"));
162                         // Select range tab
163                         _tabs.setSelectedIndex(1);
164                 }
165
166                 Selection selection = _app.getTrackInfo().getSelection();
167                 if (selection.hasRangeSelected())
168                 {
169                         RangeStatsWithGradients stats = new RangeStatsWithGradients(_app.getTrackInfo().getTrack(),
170                                 selection.getStart(), selection.getEnd());
171                         SpeedValue maxSpeed = calculateMaxSpeed(_app.getTrackInfo().getTrack(),
172                                 selection.getStart(), selection.getEnd());
173                         _rangeTextArea.setText(makeRangeDescription(stats, maxSpeed));
174                 }
175                 else
176                 {
177                         _rangeTextArea.setText(I18nManager.getText("details.norangeselection"));
178                 }
179         }
180
181         /**
182          * Calculate the maximum horizontal speed value in the given selection
183          * @param inTrack track object
184          * @param inStartIndex start of selection
185          * @param inEndIndex end of selection
186          * @return max speed, if any
187          */
188         private static SpeedValue calculateMaxSpeed(Track inTrack, int inStartIndex, int inEndIndex)
189         {
190                 SpeedValue maxSpeed = new SpeedValue();
191                 SpeedValue currSpeed = new SpeedValue();
192                 for (int i=inStartIndex; i<=inEndIndex; i++)
193                 {
194                         SpeedCalculator.calculateSpeed(inTrack, i, currSpeed);
195                         if (currSpeed.isValid() && (!maxSpeed.isValid() || currSpeed.getValue() > maxSpeed.getValue()))
196                         {
197                                 maxSpeed.setValue(currSpeed.getValue());
198                         }
199                 }
200                 return maxSpeed;
201         }
202
203         /**
204          * @param inTrack current track
205          * @param inPointIndex current point index
206          * @return string describing point details
207          */
208         private static String makePointDescription(Track inTrack, int inPointIndex)
209         {
210                 DataPoint point = inTrack.getPoint(inPointIndex);
211                 if (point == null)
212                 {
213                         return "";
214                 }
215
216                 final int coordDisplayFormat = Coordinate.getCoordinateFormatForDisplay(
217                         Config.getConfigInt(Config.KEY_COORD_DISPLAY_FORMAT));
218                 StringBuffer result = new StringBuffer();
219                 final String latStr = CoordDisplay.makeCoordinateLabel(point.getLatitude(), coordDisplayFormat);
220                 final String lonStr = CoordDisplay.makeCoordinateLabel(point.getLongitude(), coordDisplayFormat);
221                 addTextPair(result, "fieldname.latitude", latStr);
222                 addTextPair(result, "fieldname.longitude", lonStr);
223                 addTextPair(result, "fieldname.coordinates", latStr + ", " + lonStr);
224
225                 if (point.hasAltitude())
226                 {
227                         final Unit altUnit = Config.getUnitSet().getAltitudeUnit();
228                         addTextPair(result, "fieldname.altitude", "" + point.getAltitude().getValue(altUnit),
229                                 I18nManager.getText(altUnit.getShortnameKey()));
230                 }
231                 if (point.hasTimestamp())
232                 {
233                         TimeZone timezone = TimezoneHelper.getSelectedTimezone();
234                         addTextPair(result, "fieldname.date", point.getTimestamp().getDateText(timezone));
235                         addTextPair(result, "fieldname.timestamp", point.getTimestamp().getTimeText(timezone));
236                 }
237
238                 addTextPair(result, "fieldname.waypointname", point.getWaypointName());
239
240                 addTextPair(result, "fieldname.description", point.getFieldValue(Field.DESCRIPTION));
241
242                 addTextPair(result, "fieldname.waypointtype", point.getFieldValue(Field.WAYPT_TYPE));
243
244                 // Speed can come from either timestamps and distances, or speed values in data
245                 SpeedValue speedValue = new SpeedValue();
246                 SpeedCalculator.calculateSpeed(inTrack, inPointIndex, speedValue);
247                 UnitSet unitSet = Config.getUnitSet();
248                 if (speedValue.isValid())
249                 {
250                         final String speedUnitsStr = I18nManager.getText(unitSet.getSpeedUnit().getShortnameKey());
251                         String speed = DisplayUtils.roundedNumber(speedValue.getValue());
252                         addTextPair(result, "fieldname.speed", speed, speedUnitsStr);
253                 }
254
255                 // Now do the vertical speed in the same way
256                 SpeedCalculator.calculateVerticalSpeed(inTrack, inPointIndex, speedValue);
257                 if (speedValue.isValid())
258                 {
259                         final String vSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
260                         String speed = DisplayUtils.roundedNumber(speedValue.getValue());
261                         addTextPair(result, "fieldname.verticalspeed", speed, vSpeedUnitsStr);
262                 }
263
264                 Photo currentPhoto = point.getPhoto();
265                 if (currentPhoto != null)
266                 {
267                         addTextPair(result, "details.photofile", currentPhoto.getName());
268                         addTextPair(result, "details.media.fullpath", currentPhoto.getFullPath());
269                 }
270
271                 AudioClip currentAudio = point.getAudio();
272                 if (currentAudio != null)
273                 {
274                         addTextPair(result, "details.audio.file", currentAudio.getName());
275                         addTextPair(result, "details.media.fullpath", currentAudio.getFullPath());
276                 }
277
278                 return result.toString();
279         }
280
281         /**
282          * Make the range description text
283          * @param inStats stats object
284          * @param inMaxSpeed maximum speed info
285          * @return string describing range
286          */
287         private static String makeRangeDescription(RangeStatsWithGradients inStats, SpeedValue inMaxSpeed)
288         {
289                 StringBuffer result = new StringBuffer();
290                 addTextPair(result, "details.track.points", "" + inStats.getNumPoints());
291                 addTextPair(result, "details.range.numsegments", "" + inStats.getNumSegments());
292                 final boolean hasMultipleSegments = (inStats.getNumSegments() > 1);
293
294                 UnitSet unitSet = Config.getUnitSet();
295                 final String speedUnitsStr = I18nManager.getText(unitSet.getSpeedUnit().getShortnameKey());
296                 if (inMaxSpeed.isValid())
297                 {
298                         final String maxSpeedStr = DisplayUtils.roundedNumber(inMaxSpeed.getValue()) + " " + speedUnitsStr;
299                         addTextPair(result, "details.range.maxspeed", maxSpeedStr);
300                 }
301
302                 addHeading(result, "dialog.fullrangedetails.colsegments");
303                 final Unit distUnit = Config.getUnitSet().getDistanceUnit();
304                 final String distUnitsStr = I18nManager.getText(distUnit.getShortnameKey());
305                 final double movingDist = inStats.getMovingDistance();
306                 addTextPair(result, "fieldname.distance", DisplayUtils.roundedNumber(movingDist),
307                         distUnitsStr);
308                 long numSecs = inStats.getMovingDurationInSeconds();
309                 addTextPair(result, "fieldname.duration", DisplayUtils.buildDurationString(numSecs));
310
311                 if (numSecs > 0 && movingDist > 0.0)
312                 {
313                         addTextPair(result, "details.range.avespeed", DisplayUtils.roundedNumber(movingDist/numSecs*3600.0),
314                                 speedUnitsStr);
315                         addTextPair(result, "details.range.pace", DisplayUtils.buildDurationString((long) (numSecs/movingDist)),
316                                 "/ " + distUnitsStr);
317                 }
318                 final Unit altUnit = unitSet.getAltitudeUnit();
319                 final String altUnitsStr = I18nManager.getText(altUnit.getShortnameKey());
320                 if (inStats.getMovingAltitudeRange().hasRange())
321                 {
322                         AltitudeRange altRange = inStats.getMovingAltitudeRange();
323                         addTextPair(result, "fieldname.altitude", "" + altRange.getMinimum(altUnit) + altUnitsStr + " "
324                                 + I18nManager.getText("details.altitude.to") + " "
325                                 + altRange.getMaximum(altUnit) + altUnitsStr);
326                         addTextPair(result, "details.range.climb", "" + altRange.getClimb(altUnit), altUnitsStr);
327                         addTextPair(result, "details.range.descent", "" + altRange.getDescent(altUnit), altUnitsStr);
328                         addTextPair(result, "details.range.gradient", DisplayUtils.formatOneDp(inStats.getMovingGradient()), "%");
329                         if (numSecs > 0)
330                         {
331                                 final String vertSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
332                                 final String vertSpeedStr = DisplayUtils.roundedNumber(inStats.getMovingVerticalSpeed());
333                                 addTextPair(result, "fieldname.verticalspeed", vertSpeedStr, vertSpeedUnitsStr);
334                         }
335                 }
336
337                 if (hasMultipleSegments)
338                 {
339                         addHeading(result, "dialog.fullrangedetails.coltotal");
340                         final double totalDist = inStats.getTotalDistance();
341                         addTextPair(result, "fieldname.distance", DisplayUtils.roundedNumber(totalDist), distUnitsStr);
342                         long totalSecs = inStats.getTotalDurationInSeconds();
343                         addTextPair(result, "fieldname.duration", DisplayUtils.buildDurationString(totalSecs));
344                         if (totalSecs > 0 && totalDist > 0.0)
345                         {
346                                 addTextPair(result, "details.range.avespeed", DisplayUtils.roundedNumber(totalDist/totalSecs*3600.0),
347                                         speedUnitsStr);
348                                 addTextPair(result, "details.range.pace", DisplayUtils.buildDurationString((long) (totalSecs/totalDist)),
349                                         "/ " + distUnitsStr);
350                         }
351                         if (inStats.getTotalAltitudeRange().hasRange())
352                         {
353                                 AltitudeRange altRange = inStats.getTotalAltitudeRange();
354                                 addTextPair(result, "details.range.climb", "" + altRange.getClimb(altUnit), altUnitsStr);
355                                 addTextPair(result, "details.range.descent", "" + altRange.getDescent(altUnit), altUnitsStr);
356                                 addTextPair(result, "details.range.gradient", DisplayUtils.formatOneDp(inStats.getTotalGradient()), "%");
357                                 if (totalSecs > 0)
358                                 {
359                                         final String vertSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
360                                         final String vertSpeedStr = DisplayUtils.roundedNumber(inStats.getTotalVerticalSpeed());
361                                         addTextPair(result, "fieldname.verticalspeed", vertSpeedStr, vertSpeedUnitsStr);
362                                 }
363                         }
364                 }
365                 return result.toString();
366         }
367
368         /**
369          * Add the label and value to the buffer
370          * @param inBuffer buffer to append to
371          * @param inLabelKey label key
372          * @param inValue value text
373          */
374         private static void addTextPair(StringBuffer inBuffer, String inLabelKey, String inValue)
375         {
376                 addTextPair(inBuffer, inLabelKey, inValue, null);
377         }
378
379         /**
380          * Add the label and value to the buffer
381          * @param inBuffer buffer to append to
382          * @param inLabelKey label key
383          * @param inValue value text
384          * @param inUnits optional units string
385          */
386         private static void addTextPair(StringBuffer inBuffer, String inLabelKey, String inValue, String inUnits)
387         {
388                 if (inValue != null && !inValue.equals(""))
389                 {
390                         inBuffer.append(I18nManager.getText(inLabelKey));
391                         inBuffer.append(": ");
392                         inBuffer.append(inValue);
393                         if (inUnits != null && !inUnits.equals(""))
394                         {
395                                 inBuffer.append(' ');
396                                 inBuffer.append(inUnits);
397                         }
398                         inBuffer.append("\n");
399                 }
400         }
401
402         /**
403          * Add a heading to the buffer
404          * @param inBuffer buffer to append to
405          * @param inLabelKey key for heading
406          */
407         private static void addHeading(StringBuffer inBuffer, String inLabelKey)
408         {
409                 final String heading = I18nManager.getText(inLabelKey);
410                 inBuffer.append('\n').append(heading).append('\n');
411                 for (int i=0; i<heading.length(); i++)
412                 {
413                         inBuffer.append('=');
414                 }
415                 inBuffer.append('\n');
416         }
417 }