]> gitweb.fperrin.net Git - GpsPrune.git/blob - src/tim/prune/function/ShowFullDetails.java
Version 20.1, December 2020
[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.comment", point.getFieldValue(Field.COMMENT));
243
244                 addTextPair(result, "fieldname.waypointtype", point.getFieldValue(Field.WAYPT_TYPE));
245
246                 // Speed can come from either timestamps and distances, or speed values in data
247                 SpeedValue speedValue = new SpeedValue();
248                 SpeedCalculator.calculateSpeed(inTrack, inPointIndex, speedValue);
249                 UnitSet unitSet = Config.getUnitSet();
250                 if (speedValue.isValid())
251                 {
252                         final String speedUnitsStr = I18nManager.getText(unitSet.getSpeedUnit().getShortnameKey());
253                         String speed = DisplayUtils.roundedNumber(speedValue.getValue());
254                         addTextPair(result, "fieldname.speed", speed, speedUnitsStr);
255                 }
256
257                 // Now do the vertical speed in the same way
258                 SpeedCalculator.calculateVerticalSpeed(inTrack, inPointIndex, speedValue);
259                 if (speedValue.isValid())
260                 {
261                         final String vSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
262                         String speed = DisplayUtils.roundedNumber(speedValue.getValue());
263                         addTextPair(result, "fieldname.verticalspeed", speed, vSpeedUnitsStr);
264                 }
265
266                 Photo currentPhoto = point.getPhoto();
267                 if (currentPhoto != null)
268                 {
269                         addTextPair(result, "details.photofile", currentPhoto.getName());
270                         addTextPair(result, "details.media.fullpath", currentPhoto.getFullPath());
271                 }
272
273                 AudioClip currentAudio = point.getAudio();
274                 if (currentAudio != null)
275                 {
276                         addTextPair(result, "details.audio.file", currentAudio.getName());
277                         addTextPair(result, "details.media.fullpath", currentAudio.getFullPath());
278                 }
279
280                 return result.toString();
281         }
282
283         /**
284          * Make the range description text
285          * @param inStats stats object
286          * @param inMaxSpeed maximum speed info
287          * @return string describing range
288          */
289         private static String makeRangeDescription(RangeStatsWithGradients inStats, SpeedValue inMaxSpeed)
290         {
291                 StringBuffer result = new StringBuffer();
292                 addTextPair(result, "details.track.points", "" + inStats.getNumPoints());
293                 addTextPair(result, "details.range.numsegments", "" + inStats.getNumSegments());
294                 final boolean hasMultipleSegments = (inStats.getNumSegments() > 1);
295
296                 UnitSet unitSet = Config.getUnitSet();
297                 final String speedUnitsStr = I18nManager.getText(unitSet.getSpeedUnit().getShortnameKey());
298                 if (inMaxSpeed.isValid())
299                 {
300                         final String maxSpeedStr = DisplayUtils.roundedNumber(inMaxSpeed.getValue()) + " " + speedUnitsStr;
301                         addTextPair(result, "details.range.maxspeed", maxSpeedStr);
302                 }
303
304                 addHeading(result, "dialog.fullrangedetails.colsegments");
305                 final Unit distUnit = Config.getUnitSet().getDistanceUnit();
306                 final String distUnitsStr = I18nManager.getText(distUnit.getShortnameKey());
307                 final double movingDist = inStats.getMovingDistance();
308                 addTextPair(result, "fieldname.distance", DisplayUtils.roundedNumber(movingDist),
309                         distUnitsStr);
310                 long numSecs = inStats.getMovingDurationInSeconds();
311                 addTextPair(result, "fieldname.duration", DisplayUtils.buildDurationString(numSecs));
312
313                 if (numSecs > 0 && movingDist > 0.0)
314                 {
315                         addTextPair(result, "details.range.avespeed", DisplayUtils.roundedNumber(movingDist/numSecs*3600.0),
316                                 speedUnitsStr);
317                         addTextPair(result, "details.range.pace", DisplayUtils.buildDurationString((long) (numSecs/movingDist)),
318                                 "/ " + distUnitsStr);
319                 }
320                 final Unit altUnit = unitSet.getAltitudeUnit();
321                 final String altUnitsStr = I18nManager.getText(altUnit.getShortnameKey());
322                 if (inStats.getMovingAltitudeRange().hasRange())
323                 {
324                         AltitudeRange altRange = inStats.getMovingAltitudeRange();
325                         addTextPair(result, "fieldname.altitude", "" + altRange.getMinimum(altUnit) + altUnitsStr + " "
326                                 + I18nManager.getText("details.altitude.to") + " "
327                                 + altRange.getMaximum(altUnit) + altUnitsStr);
328                         addTextPair(result, "details.range.climb", "" + altRange.getClimb(altUnit), altUnitsStr);
329                         addTextPair(result, "details.range.descent", "" + altRange.getDescent(altUnit), altUnitsStr);
330                         addTextPair(result, "details.range.gradient", DisplayUtils.formatOneDp(inStats.getMovingGradient()), "%");
331                         if (numSecs > 0)
332                         {
333                                 final String vertSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
334                                 final String vertSpeedStr = DisplayUtils.roundedNumber(inStats.getMovingVerticalSpeed());
335                                 addTextPair(result, "fieldname.verticalspeed", vertSpeedStr, vertSpeedUnitsStr);
336                         }
337                 }
338
339                 if (hasMultipleSegments)
340                 {
341                         addHeading(result, "dialog.fullrangedetails.coltotal");
342                         final double totalDist = inStats.getTotalDistance();
343                         addTextPair(result, "fieldname.distance", DisplayUtils.roundedNumber(totalDist), distUnitsStr);
344                         long totalSecs = inStats.getTotalDurationInSeconds();
345                         addTextPair(result, "fieldname.duration", DisplayUtils.buildDurationString(totalSecs));
346                         if (totalSecs > 0 && totalDist > 0.0)
347                         {
348                                 addTextPair(result, "details.range.avespeed", DisplayUtils.roundedNumber(totalDist/totalSecs*3600.0),
349                                         speedUnitsStr);
350                                 addTextPair(result, "details.range.pace", DisplayUtils.buildDurationString((long) (totalSecs/totalDist)),
351                                         "/ " + distUnitsStr);
352                         }
353                         if (inStats.getTotalAltitudeRange().hasRange())
354                         {
355                                 AltitudeRange altRange = inStats.getTotalAltitudeRange();
356                                 addTextPair(result, "details.range.climb", "" + altRange.getClimb(altUnit), altUnitsStr);
357                                 addTextPair(result, "details.range.descent", "" + altRange.getDescent(altUnit), altUnitsStr);
358                                 addTextPair(result, "details.range.gradient", DisplayUtils.formatOneDp(inStats.getTotalGradient()), "%");
359                                 if (totalSecs > 0)
360                                 {
361                                         final String vertSpeedUnitsStr = I18nManager.getText(unitSet.getVerticalSpeedUnit().getShortnameKey());
362                                         final String vertSpeedStr = DisplayUtils.roundedNumber(inStats.getTotalVerticalSpeed());
363                                         addTextPair(result, "fieldname.verticalspeed", vertSpeedStr, vertSpeedUnitsStr);
364                                 }
365                         }
366                 }
367                 return result.toString();
368         }
369
370         /**
371          * Add the label and value to the buffer
372          * @param inBuffer buffer to append to
373          * @param inLabelKey label key
374          * @param inValue value text
375          */
376         private static void addTextPair(StringBuffer inBuffer, String inLabelKey, String inValue)
377         {
378                 addTextPair(inBuffer, inLabelKey, inValue, null);
379         }
380
381         /**
382          * Add the label and value to the buffer
383          * @param inBuffer buffer to append to
384          * @param inLabelKey label key
385          * @param inValue value text
386          * @param inUnits optional units string
387          */
388         private static void addTextPair(StringBuffer inBuffer, String inLabelKey, String inValue, String inUnits)
389         {
390                 if (inValue != null && !inValue.equals(""))
391                 {
392                         inBuffer.append(I18nManager.getText(inLabelKey));
393                         inBuffer.append(": ");
394                         inBuffer.append(inValue);
395                         if (inUnits != null && !inUnits.equals(""))
396                         {
397                                 inBuffer.append(' ');
398                                 inBuffer.append(inUnits);
399                         }
400                         inBuffer.append("\n");
401                 }
402         }
403
404         /**
405          * Add a heading to the buffer
406          * @param inBuffer buffer to append to
407          * @param inLabelKey key for heading
408          */
409         private static void addHeading(StringBuffer inBuffer, String inLabelKey)
410         {
411                 final String heading = I18nManager.getText(inLabelKey);
412                 inBuffer.append('\n').append(heading).append('\n');
413                 for (int i=0; i<heading.length(); i++)
414                 {
415                         inBuffer.append('=');
416                 }
417                 inBuffer.append('\n');
418         }
419 }