]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - src/tim/prune/function/estimate/EstimateTime.java
Moved source into separate src directory due to popular request
[GpsPrune.git] / src / tim / prune / function / estimate / EstimateTime.java
diff --git a/src/tim/prune/function/estimate/EstimateTime.java b/src/tim/prune/function/estimate/EstimateTime.java
new file mode 100644 (file)
index 0000000..ef69793
--- /dev/null
@@ -0,0 +1,377 @@
+package tim.prune.function.estimate;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.config.Config;
+import tim.prune.data.RangeStats;
+import tim.prune.data.Selection;
+import tim.prune.data.Unit;
+import tim.prune.gui.DecimalNumberField;
+import tim.prune.gui.DisplayUtils;
+import tim.prune.gui.GuiGridLayout;
+import tim.prune.tips.TipManager;
+
+/**
+ * Class to calculate and show the results of estimating (hike) time for the current range
+ */
+public class EstimateTime extends GenericFunction
+{
+       /** Dialog */
+       private JDialog _dialog = null;
+       /** Labels for distances */
+       private JLabel _distanceLabel = null;
+       /** Labels for durations */
+       private JLabel _estimatedDurationLabel = null, _actualDurationLabel = null;
+       /** Labels for climbs */
+       private JLabel _gentleClimbLabel = null, _steepClimbLabel = null;
+       /** Labels for descents */
+       private JLabel _gentleDescentLabel = null, _steepDescentLabel = null;
+       /** Labels and text fields for parameters */
+       private JLabel _flatSpeedLabel = null;
+       private DecimalNumberField _flatSpeedField = null;
+       private JLabel _climbParamLabel = null;
+       private DecimalNumberField _gentleClimbField = null, _steepClimbField = null;
+       private JLabel _descentParamLabel = null;
+       private DecimalNumberField _gentleDescentField = null, _steepDescentField = null;
+       /** Range stats */
+       private RangeStats _stats = null;
+
+
+       /**
+        * Constructor
+        * @param inApp App object
+        */
+       public EstimateTime(App inApp)
+       {
+               super(inApp);
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.estimatetime";
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               // Get the stats on the selection before launching the dialog
+               Selection selection = _app.getTrackInfo().getSelection();
+               _stats = new RangeStats(_app.getTrackInfo().getTrack(), selection.getStart(), selection.getEnd());
+
+               if (_stats.getMovingDistance() < 0.01)
+               {
+                       _app.showErrorMessage(getNameKey(), "dialog.estimatetime.error.nodistance");
+                       return;
+               }
+               if (_dialog == null)
+               {
+                       // First time in, check whether params are at default, show tip message if unaltered
+                       showTip();
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                       _dialog.getContentPane().add(makeDialogComponents());
+                       _dialog.pack();
+               }
+               updateDetails();
+               _dialog.setVisible(true);
+       }
+
+       /**
+        * Create dialog components
+        * @return Panel containing all gui elements in dialog
+        */
+       private Component makeDialogComponents()
+       {
+               JPanel dialogPanel = new JPanel();
+               dialogPanel.setLayout(new BorderLayout(5, 5));
+
+               // main panel with a box layout
+               JPanel mainPanel = new JPanel();
+               mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
+               // Label at top
+               JLabel introLabel = new JLabel(I18nManager.getText("dialog.fullrangedetails.intro") + ":");
+               introLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+               introLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+               mainPanel.add(introLabel);
+               mainPanel.add(Box.createVerticalStrut(4));
+
+               // Details panel in a grid
+               JPanel detailsPanel = new JPanel();
+               detailsPanel.setLayout(new GridLayout(0, 4, 6, 2));
+               detailsPanel.setBorder(BorderFactory.createTitledBorder(
+                       I18nManager.getText("dialog.estimatetime.details")));
+
+               // Distance
+               JLabel distLabel = new JLabel(I18nManager.getText("fieldname.distance") + ": ");
+               distLabel.setHorizontalAlignment(JLabel.RIGHT);
+               detailsPanel.add(distLabel);
+               _distanceLabel = new JLabel("5 km");
+               detailsPanel.add(_distanceLabel);
+               detailsPanel.add(new JLabel("")); detailsPanel.add(new JLabel("")); // two blank cells
+
+               detailsPanel.add(new JLabel(""));
+               detailsPanel.add(new JLabel(I18nManager.getText("dialog.estimatetime.gentle")));
+               detailsPanel.add(new JLabel(I18nManager.getText("dialog.estimatetime.steep")));
+               detailsPanel.add(new JLabel("")); // blank cells
+
+               // Climb
+               JLabel climbLabel = new JLabel(I18nManager.getText("dialog.estimatetime.climb") + ": ");
+               climbLabel.setHorizontalAlignment(JLabel.RIGHT);
+               detailsPanel.add(climbLabel);
+               _gentleClimbLabel = new JLabel("1500 m");
+               detailsPanel.add(_gentleClimbLabel);
+               _steepClimbLabel = new JLabel("1500 m");
+               detailsPanel.add(_steepClimbLabel);
+               detailsPanel.add(new JLabel(""));
+
+               // Descent
+               JLabel descentLabel = new JLabel(I18nManager.getText("dialog.estimatetime.descent") + ": ");
+               descentLabel.setHorizontalAlignment(JLabel.RIGHT);
+               detailsPanel.add(descentLabel);
+               _gentleDescentLabel = new JLabel("1500 m");
+               detailsPanel.add(_gentleDescentLabel);
+               _steepDescentLabel = new JLabel("1500 m");
+               detailsPanel.add(_steepDescentLabel);
+               detailsPanel.add(new JLabel(""));
+
+               detailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+               mainPanel.add(detailsPanel);
+               mainPanel.add(Box.createVerticalStrut(4));
+
+               // Parameters panel in a flexible grid
+               JPanel paramsPanel = new JPanel();
+               GuiGridLayout paramsGrid = new GuiGridLayout(paramsPanel, new double[] {1.5, 0.2, 1.0, 0.2, 0.5},
+                       new boolean[] {true, false, false, false, false});
+               paramsPanel.setBorder(BorderFactory.createTitledBorder(
+                       I18nManager.getText("dialog.estimatetime.parameters")));
+               KeyAdapter paramChangeListener = new KeyAdapter() {
+                       public void keyTyped(KeyEvent inE) {
+                               SwingUtilities.invokeLater(new Runnable() {
+                                       public void run() {
+                                               calculateEstimatedTime();
+                                       }
+                               });
+                       }
+                       public void keyPressed(KeyEvent inE) {
+                               if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
+                       }
+               };
+
+               // Flat speed
+               _flatSpeedLabel = new JLabel(I18nManager.getText("dialog.estimatetime.parameters.timefor") + ": "); // (filled in later)
+               _flatSpeedLabel.setHorizontalAlignment(JLabel.RIGHT);
+               paramsGrid.add(_flatSpeedLabel);
+               _flatSpeedField = new DecimalNumberField();
+               _flatSpeedField.addKeyListener(paramChangeListener);
+               paramsGrid.add(_flatSpeedField);
+               JLabel minsLabel = new JLabel(I18nManager.getText("units.minutes"));
+               paramsGrid.add(minsLabel);
+               paramsGrid.nextRow();
+               // Headers for gentle and steep
+               paramsGrid.add(new JLabel(""));
+               paramsGrid.add(new JLabel(I18nManager.getText("dialog.estimatetime.gentle")));
+               paramsGrid.add(new JLabel("")); // blank cell
+               paramsGrid.add(new JLabel(I18nManager.getText("dialog.estimatetime.steep")));
+               paramsGrid.nextRow();
+               // Gentle climb
+               _climbParamLabel = new JLabel(I18nManager.getText("dialog.estimatetime.parameters.timefor") + ": "); // (filled in later)
+               _climbParamLabel.setHorizontalAlignment(JLabel.RIGHT);
+               paramsGrid.add(_climbParamLabel);
+               _gentleClimbField = new DecimalNumberField();
+               _gentleClimbField.addKeyListener(paramChangeListener);
+               paramsGrid.add(_gentleClimbField);
+               paramsGrid.add(new JLabel(minsLabel.getText()));
+               // Steep climb
+               _steepClimbField = new DecimalNumberField();
+               _steepClimbField.addKeyListener(paramChangeListener);
+               paramsGrid.add(_steepClimbField);
+               paramsGrid.add(new JLabel(minsLabel.getText()));
+
+               // Gentle descent
+               _descentParamLabel = new JLabel(I18nManager.getText("dialog.estimatetime.parameters.timefor") + ": "); // (filled in later)
+               _descentParamLabel.setHorizontalAlignment(JLabel.RIGHT);
+               paramsGrid.add(_descentParamLabel);
+               _gentleDescentField = new DecimalNumberField(true); // negative numbers allowed
+               _gentleDescentField.addKeyListener(paramChangeListener);
+               paramsGrid.add(_gentleDescentField);
+               paramsGrid.add(new JLabel(minsLabel.getText()));
+               // Steep climb
+               _steepDescentField = new DecimalNumberField(true); // negative numbers allowed
+               _steepDescentField.addKeyListener(paramChangeListener);
+               paramsGrid.add(_steepDescentField);
+               paramsGrid.add(new JLabel(minsLabel.getText()));
+
+               paramsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+               mainPanel.add(paramsPanel);
+               mainPanel.add(Box.createVerticalStrut(12));
+
+               // Results panel
+               JPanel resultsPanel = new JPanel();
+               resultsPanel.setBorder(BorderFactory.createTitledBorder(
+                       I18nManager.getText("dialog.estimatetime.results")));
+               resultsPanel.setLayout(new GridLayout(0, 2, 3, 3));
+               // estimated time
+               _estimatedDurationLabel = new JLabel(I18nManager.getText("dialog.estimatetime.results.estimatedtime") + ": "); // filled in later
+               Font origFont = _estimatedDurationLabel.getFont();
+               _estimatedDurationLabel.setFont(origFont.deriveFont(Font.BOLD, origFont.getSize2D() + 2.0f));
+
+               resultsPanel.add(_estimatedDurationLabel);
+               // actual time (if available)
+               _actualDurationLabel = new JLabel(I18nManager.getText("dialog.estimatetime.results.actualtime") + ": "); // filled in later
+               resultsPanel.add(_actualDurationLabel);
+
+               resultsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+               mainPanel.add(resultsPanel);
+               mainPanel.add(Box.createVerticalStrut(4));
+
+               dialogPanel.add(mainPanel, BorderLayout.NORTH);
+               // button panel at bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               JButton closeButton = new JButton(I18nManager.getText("button.close"));
+               closeButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               finishDialog();
+                       }
+               });
+               closeButton.addKeyListener(new KeyAdapter() {
+                       public void keyPressed(KeyEvent inE) {
+                               if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
+                       }
+               });
+               buttonPanel.add(closeButton);
+               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+               return dialogPanel;
+       }
+
+
+       /**
+        * Recalculate the values and update the labels
+        */
+       private void updateDetails()
+       {
+               // Warn if the current track hasn't got any height information
+               if (!_stats.getMovingAltitudeRange().hasRange()) {
+                       _app.showErrorMessage(getNameKey(), "dialog.estimatetime.error.noaltitudes");
+               }
+
+               // Distance in current units
+               final Unit distUnit = Config.getUnitSet().getDistanceUnit();
+               final String distUnitsStr = I18nManager.getText(distUnit.getShortnameKey());
+               final double movingDist = _stats.getMovingDistance();
+               _distanceLabel.setText(DisplayUtils.roundedNumber(movingDist) + " " + distUnitsStr);
+
+               // Climb and descent values
+               final Unit altUnit = Config.getUnitSet().getAltitudeUnit();
+               final String altUnitsStr = " " + I18nManager.getText(altUnit.getShortnameKey());
+               _gentleClimbLabel.setText(_stats.getGentleAltitudeRange().getClimb(altUnit) + altUnitsStr);
+               _steepClimbLabel.setText(_stats.getSteepAltitudeRange().getClimb(altUnit) + altUnitsStr);
+               _gentleDescentLabel.setText(_stats.getGentleAltitudeRange().getDescent(altUnit) + altUnitsStr);
+               _steepDescentLabel.setText(_stats.getSteepAltitudeRange().getDescent(altUnit) + altUnitsStr);
+
+               // Try to get parameters from config
+               EstimationParameters estParams = new EstimationParameters(Config.getConfigString(Config.KEY_ESTIMATION_PARAMS));
+
+               String[] paramValues = estParams.getStrings();
+               if (paramValues == null || paramValues.length != 5) {return;} // TODO: What to do in case of error?
+               // Flat time is either for 5 km, 3 miles or 3 nm
+               _flatSpeedLabel.setText(I18nManager.getText("dialog.estimatetime.parameters.timefor") +
+                       " " + EstimationParameters.getStandardDistance() + ": ");
+               _flatSpeedField.setText(paramValues[0]);
+
+               final String heightString = " " + EstimationParameters.getStandardClimb() + ": ";
+               _climbParamLabel.setText(I18nManager.getText("dialog.estimatetime.climb") + heightString);
+               _gentleClimbField.setText(paramValues[1]);
+               _steepClimbField.setText(paramValues[2]);
+               _descentParamLabel.setText(I18nManager.getText("dialog.estimatetime.descent") + heightString);
+               _gentleDescentField.setText(paramValues[3]);
+               _steepDescentField.setText(paramValues[4]);
+
+               // Use the entered parameters to estimate the time
+               calculateEstimatedTime();
+
+               // Get the actual time if available, for comparison
+               if (_stats.getMovingDurationInSeconds() > 0)
+               {
+                       _actualDurationLabel.setText(I18nManager.getText("dialog.estimatetime.results.actualtime") + ": "
+                               + DisplayUtils.buildDurationString(_stats.getMovingDurationInSeconds()));
+               }
+               else {
+                       _actualDurationLabel.setText("");
+               }
+       }
+
+
+       /**
+        * Use the current parameter and the range stats to calculate the estimated time
+        * and populate the answer in the dialog
+        */
+       private void calculateEstimatedTime()
+       {
+               // Populate an EstimationParameters object from the four strings
+               EstimationParameters params = new EstimationParameters();
+               params.populateWithStrings(_flatSpeedField.getText(), _gentleClimbField.getText(),
+                       _steepClimbField.getText(), _gentleDescentField.getText(), _steepDescentField.getText());
+               if (!params.isValid()) {
+                       _estimatedDurationLabel.setText("- - -");
+               }
+               else
+               {
+                       final long numSeconds = (long) (params.applyToStats(_stats) * 60.0);
+                       _estimatedDurationLabel.setText(I18nManager.getText("dialog.estimatetime.results.estimatedtime") + ": "
+                               + DisplayUtils.buildDurationString(numSeconds));
+               }
+       }
+
+
+       /**
+        * Finish with the dialog, by pressing the "Close" button
+        */
+       private void finishDialog()
+       {
+               // Make estimation parameters from entered strings, if valid save to config
+               EstimationParameters params = new EstimationParameters();
+               params.populateWithStrings(_flatSpeedField.getText(), _gentleClimbField.getText(),
+                       _steepClimbField.getText(), _gentleDescentField.getText(), _steepDescentField.getText());
+               if (params.isValid()) {
+                       Config.setConfigString(Config.KEY_ESTIMATION_PARAMS, params.toConfigString());
+               }
+               _dialog.dispose();
+       }
+
+       /**
+        * Show a tip to use the learn function, if appropriate
+        */
+       private void showTip()
+       {
+               EstimationParameters currParams = new EstimationParameters(
+                       Config.getConfigString(Config.KEY_ESTIMATION_PARAMS));
+               if (currParams.sameAsDefaults()) {
+                       _app.showTip(TipManager.Tip_LearnTimeParams);
+               }
+       }
+}