X-Git-Url: http://gitweb.fperrin.net/?p=GpsPrune.git;a=blobdiff_plain;f=src%2Ftim%2Fprune%2Ffunction%2Festimate%2FEstimateTime.java;fp=src%2Ftim%2Fprune%2Ffunction%2Festimate%2FEstimateTime.java;h=ef6979358edaefb64828469f18a2c41e6daf8846;hp=0000000000000000000000000000000000000000;hb=ce6f2161b8596f7018d6a76bff79bc9e571f35fd;hpb=2d8cb72e84d5cc1089ce77baf1e34ea3ea2f8465 diff --git a/src/tim/prune/function/estimate/EstimateTime.java b/src/tim/prune/function/estimate/EstimateTime.java new file mode 100644 index 0000000..ef69793 --- /dev/null +++ b/src/tim/prune/function/estimate/EstimateTime.java @@ -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); + } + } +}