]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/function/charts/Charter.java
Moved source into separate src directory due to popular request
[GpsPrune.git] / tim / prune / function / charts / Charter.java
diff --git a/tim/prune/function/charts/Charter.java b/tim/prune/function/charts/Charter.java
deleted file mode 100644 (file)
index 5b7ae83..0000000
+++ /dev/null
@@ -1,620 +0,0 @@
-package tim.prune.function.charts;
-
-import java.awt.BorderLayout;
-import java.awt.FlowLayout;
-import java.awt.GridLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-
-import javax.swing.BorderFactory;
-import javax.swing.BoxLayout;
-import javax.swing.ButtonGroup;
-import javax.swing.JButton;
-import javax.swing.JCheckBox;
-import javax.swing.JDialog;
-import javax.swing.JFileChooser;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JRadioButton;
-import javax.swing.JTextField;
-import javax.swing.SwingConstants;
-
-import tim.prune.App;
-import tim.prune.ExternalTools;
-import tim.prune.GenericFunction;
-import tim.prune.I18nManager;
-import tim.prune.config.Config;
-import tim.prune.data.DataPoint;
-import tim.prune.data.Distance;
-import tim.prune.data.Field;
-import tim.prune.data.Timestamp;
-import tim.prune.data.Track;
-import tim.prune.gui.profile.SpeedData;
-import tim.prune.gui.profile.VerticalSpeedData;
-import tim.prune.load.GenericFileFilter;
-
-/**
- * Class to manage the generation of charts using gnuplot
- */
-public class Charter extends GenericFunction
-{
-       /** dialog object, cached */
-       private JDialog _dialog = null;
-       /** radio button for distance axis */
-       private JRadioButton _distanceRadio = null;
-       /** radio button for time axis */
-       private JRadioButton _timeRadio = null;
-       /** array of checkboxes for specifying y axes */
-       private JCheckBox[] _yAxesBoxes = null;
-       /** radio button for svg output */
-       private JRadioButton _svgRadio = null;
-       /** file chooser for saving svg file */
-       private JFileChooser _fileChooser = null;
-       /** text field for svg width */
-       private JTextField _svgWidthField = null;
-       /** text field for svg height */
-       private JTextField _svgHeightField = null;
-
-       /** Default dimensions of Svg file */
-       private static final String DEFAULT_SVG_WIDTH  = "800";
-       private static final String DEFAULT_SVG_HEIGHT = "400";
-
-
-       /**
-        * Constructor from superclass
-        * @param inApp app object
-        */
-       public Charter(App inApp)
-       {
-               super(inApp);
-       }
-
-       /**
-        * @return key for function name
-        */
-       public String getNameKey()
-       {
-               return "function.charts";
-       }
-
-       /**
-        * Show the dialog
-        */
-       public void begin()
-       {
-               // First check if gnuplot is available
-               if (!ExternalTools.isToolInstalled(ExternalTools.TOOL_GNUPLOT))
-               {
-                       _app.showErrorMessage(getNameKey(), "dialog.charts.gnuplotnotfound");
-                       return;
-               }
-               // Make dialog window
-               if (_dialog == null)
-               {
-                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
-                       _dialog.setLocationRelativeTo(_parentFrame);
-                       _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
-                       _dialog.getContentPane().add(makeDialogComponents());
-                       _dialog.pack();
-               }
-               if (setupDialog(_app.getTrackInfo().getTrack())) {
-                       _dialog.setVisible(true);
-               }
-               else {
-                       _app.showErrorMessage(getNameKey(), "dialog.charts.needaltitudeortimes");
-               }
-       }
-
-
-       /**
-        * Make the dialog components
-        * @return panel containing gui elements
-        */
-       private JPanel makeDialogComponents()
-       {
-               JPanel dialogPanel = new JPanel();
-               dialogPanel.setLayout(new BorderLayout());
-
-               JPanel mainPanel = new JPanel();
-               mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
-               // x axis choice
-               JPanel axisPanel = new JPanel();
-               axisPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("dialog.charts.xaxis")));
-               _distanceRadio = new JRadioButton(I18nManager.getText("fieldname.distance"));
-               _distanceRadio.setSelected(true);
-               _timeRadio = new JRadioButton(I18nManager.getText("fieldname.time"));
-               ButtonGroup axisGroup = new ButtonGroup();
-               axisGroup.add(_distanceRadio); axisGroup.add(_timeRadio);
-               axisPanel.add(_distanceRadio); axisPanel.add(_timeRadio);
-               mainPanel.add(axisPanel);
-
-               // y axis choices
-               JPanel yPanel = new JPanel();
-               yPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("dialog.charts.yaxis")));
-               _yAxesBoxes = new JCheckBox[4]; // dist altitude speed vertspeed (time not available on y axis)
-               _yAxesBoxes[0] = new JCheckBox(I18nManager.getText("fieldname.distance"));
-               _yAxesBoxes[1] = new JCheckBox(I18nManager.getText("fieldname.altitude"));
-               _yAxesBoxes[1].setSelected(true);
-               _yAxesBoxes[2] = new JCheckBox(I18nManager.getText("fieldname.speed"));
-               _yAxesBoxes[3] = new JCheckBox(I18nManager.getText("fieldname.verticalspeed"));
-               for (int i=0; i<4; i++) {
-                       yPanel.add(_yAxesBoxes[i]);
-               }
-               mainPanel.add(yPanel);
-
-               // Add validation to prevent choosing invalid (ie dist/dist) combinations
-               ActionListener xAxisListener = new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               enableYbox(0, _timeRadio.isSelected());
-                       }
-               };
-               _timeRadio.addActionListener(xAxisListener);
-               _distanceRadio.addActionListener(xAxisListener);
-
-               // output buttons
-               JPanel outputPanel = new JPanel();
-               outputPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("dialog.charts.output")));
-               outputPanel.setLayout(new BorderLayout());
-               JPanel radiosPanel = new JPanel();
-               JRadioButton screenRadio = new JRadioButton(I18nManager.getText("dialog.charts.screen"));
-               screenRadio.setSelected(true);
-               _svgRadio = new JRadioButton(I18nManager.getText("dialog.charts.svg"));
-               ButtonGroup outputGroup = new ButtonGroup();
-               outputGroup.add(screenRadio); outputGroup.add(_svgRadio);
-               radiosPanel.add(screenRadio); radiosPanel.add(_svgRadio);
-               outputPanel.add(radiosPanel, BorderLayout.NORTH);
-               // panel for svg width, height
-               JPanel sizePanel = new JPanel();
-               sizePanel.setLayout(new GridLayout(2, 2, 10, 1));
-               JLabel widthLabel = new JLabel(I18nManager.getText("dialog.charts.svgwidth"));
-               widthLabel.setHorizontalAlignment(SwingConstants.RIGHT);
-               sizePanel.add(widthLabel);
-               _svgWidthField = new JTextField(DEFAULT_SVG_WIDTH, 5);
-               sizePanel.add(_svgWidthField);
-               JLabel heightLabel = new JLabel(I18nManager.getText("dialog.charts.svgheight"));
-               heightLabel.setHorizontalAlignment(SwingConstants.RIGHT);
-               sizePanel.add(heightLabel);
-               _svgHeightField = new JTextField(DEFAULT_SVG_HEIGHT, 5);
-               sizePanel.add(_svgHeightField);
-
-               outputPanel.add(sizePanel, BorderLayout.EAST);
-               mainPanel.add(outputPanel);
-               dialogPanel.add(mainPanel, BorderLayout.CENTER);
-
-               // button panel on bottom
-               JPanel buttonPanel = new JPanel();
-               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
-               // ok button
-               JButton okButton = new JButton(I18nManager.getText("button.ok"));
-               okButton.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               showChart(_app.getTrackInfo().getTrack());
-                               _dialog.setVisible(false);
-                       }
-               });
-               buttonPanel.add(okButton);
-               // Cancel button
-               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
-               cancelButton.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               _dialog.setVisible(false);
-                       }
-               });
-               buttonPanel.add(cancelButton);
-               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
-               return dialogPanel;
-       }
-
-
-       /**
-        * Set up the dialog according to the track contents
-        * @param inTrack track object
-        * @return true if it's all ok
-        */
-       private boolean setupDialog(Track inTrack)
-       {
-               boolean hasTimes = inTrack.hasData(Field.TIMESTAMP);
-               boolean hasAltitudes = inTrack.hasAltitudeData();
-               _timeRadio.setEnabled(hasTimes);
-
-               // Add checks to prevent choosing unavailable combinations
-               if (!hasTimes) {
-                       _distanceRadio.setSelected(true);
-               }
-               enableYbox(0, !_distanceRadio.isSelected());
-               enableYbox(1, hasAltitudes);
-               enableYbox(2, hasTimes);
-               enableYbox(3, hasTimes && hasAltitudes);
-               return (hasTimes || hasAltitudes);
-       }
-
-
-       /**
-        * Enable or disable the given y axis checkbox
-        * @param inIndex index of checkbox
-        * @param inFlag true to enable
-        */
-       private void enableYbox(int inIndex, boolean inFlag)
-       {
-               _yAxesBoxes[inIndex].setEnabled(inFlag);
-               if (!inFlag) {
-                       _yAxesBoxes[inIndex].setSelected(inFlag);
-               }
-       }
-
-       /**
-        * Show the chart for the specified track
-        * @param inTrack track object containing data
-        */
-       private void showChart(Track inTrack)
-       {
-               int numCharts = 0;
-               for (int i=0; i<_yAxesBoxes.length; i++) {
-                       if (_yAxesBoxes[i].isSelected()) {
-                               numCharts++;
-                       }
-               }
-               // Select default chart if none selected
-               if (numCharts == 0) {
-                       _yAxesBoxes[1].setSelected(true);
-                       numCharts = 1;
-               }
-               int[] heights = getHeights(numCharts);
-
-               boolean showSvg = _svgRadio.isSelected();
-               File svgFile = null;
-               if (showSvg) {
-                       svgFile = selectSvgFile();
-                       if (svgFile == null) {showSvg = false;}
-               }
-               OutputStreamWriter writer = null;
-               try
-               {
-                       final String gnuplotPath = Config.getConfigString(Config.KEY_GNUPLOT_PATH);
-                       Process process = Runtime.getRuntime().exec(gnuplotPath + " -persist");
-                       writer = new OutputStreamWriter(process.getOutputStream());
-                       if (showSvg)
-                       {
-                               writer.write("set terminal svg size " + getSvgValue(_svgWidthField, DEFAULT_SVG_WIDTH) + " "
-                                       + getSvgValue(_svgHeightField, DEFAULT_SVG_HEIGHT) + "\n");
-                               writer.write("set out '" + svgFile.getAbsolutePath() + "'\n");
-                       }
-                       else {
-                               // For screen output, gnuplot should use the default terminal (windows or x11 or wxt or something)
-                       }
-                       if (numCharts > 1) {
-                               writer.write("set multiplot layout " + numCharts + ",1\n");
-                       }
-                       // Loop over possible charts
-                       int chartNum = 0;
-                       for (int c=0; c<_yAxesBoxes.length; c++)
-                       {
-                               if (_yAxesBoxes[c].isSelected())
-                               {
-                                       writer.write("set size 1," + (0.01*heights[chartNum*2+1]) + "\n");
-                                       writer.write("set origin 0," + (0.01*heights[chartNum*2]) + "\n");
-                                       writeChart(writer, inTrack, _distanceRadio.isSelected(), c);
-                                       chartNum++;
-                               }
-                       }
-                       // Close multiplot if open
-                       if (numCharts > 1) {
-                               writer.write("unset multiplot\n");
-                       }
-               }
-               catch (Exception e) {
-                       _app.showErrorMessageNoLookup(getNameKey(), e.getMessage());
-               }
-               finally {
-                       try {
-                               // Close writer
-                               if (writer != null) writer.close();
-                       }
-                       catch (Exception e) {} // ignore
-               }
-       }
-
-
-       /**
-        * Parse the given text field's value and return as string
-        * @param inField text field to read from
-        * @param inDefault default value if not valid
-        * @return value of svg dimension as string
-        */
-       private static String getSvgValue(JTextField inField, String inDefault)
-       {
-               int value = 0;
-               try {
-                       value = Integer.parseInt(inField.getText());
-               }
-               catch (Exception e) {} // ignore, value stays zero
-               if (value > 0) {
-                       return "" + value;
-               }
-               return inDefault;
-       }
-
-
-       /**
-        * Write out the selected chart to the given Writer object
-        * @param inWriter writer object
-        * @param inTrack Track containing data
-        * @param inDistance true if x axis is distance
-        * @param inYaxis index of y axis
-        * @throws IOException if writing error occurred
-        */
-       private static void writeChart(OutputStreamWriter inWriter, Track inTrack, boolean inDistance, int inYaxis)
-       throws IOException
-       {
-               ChartSeries xValues = null, yValues = null;
-               ChartSeries distValues = getDistanceValues(inTrack);
-               // Choose x values according to axis
-               if (inDistance) {
-                       xValues = distValues;
-               }
-               else {
-                       xValues = getTimeValues(inTrack);
-               }
-               // Choose y values according to axis
-               switch (inYaxis)
-               {
-               case 0: // y axis is distance
-                       yValues = distValues;
-                       break;
-               case 1: // y axis is altitude
-                       yValues = getAltitudeValues(inTrack);
-                       break;
-               case 2: // y axis is speed
-                       yValues = getSpeedValues(inTrack);
-                       break;
-               case 3: // y axis is vertical speed
-                       yValues = getVertSpeedValues(inTrack);
-                       break;
-               }
-               // Make a temporary data file for the output (one per subchart)
-               File tempFile = File.createTempFile("gpsprunedata", null);
-               tempFile.deleteOnExit();
-               // write out values for x and y to temporary file
-               FileWriter tempFileWriter = null;
-               try {
-                       tempFileWriter = new FileWriter(tempFile);
-                       tempFileWriter.write("# Temporary data file for GpsPrune charts\n\n");
-                       for (int i=0; i<inTrack.getNumPoints(); i++) {
-                               if (xValues.hasData(i) && yValues.hasData(i)) {
-                                       tempFileWriter.write("" + xValues.getData(i) + ", " + yValues.getData(i) + "\n");
-                               }
-                       }
-               }
-               catch (IOException ioe) { // rethrow
-                       throw ioe;
-               }
-               finally {
-                       try {
-                               tempFileWriter.close();
-                       }
-                       catch (Exception e) {}
-               }
-
-               // Sort out units to use
-               final String distLabel = I18nManager.getText(Config.getUnitSet().getDistanceUnit().getShortnameKey());
-               final String altLabel  = I18nManager.getText(Config.getUnitSet().getAltitudeUnit().getShortnameKey());
-               final String speedLabel = I18nManager.getText(Config.getUnitSet().getSpeedUnit().getShortnameKey());
-               final String vertSpeedLabel = I18nManager.getText(Config.getUnitSet().getVerticalSpeedUnit().getShortnameKey());
-
-               // Set x axis label
-               if (inDistance) {
-                       inWriter.write("set xlabel '" + I18nManager.getText("fieldname.distance") + " (" + distLabel + ")'\n");
-               }
-               else {
-                       inWriter.write("set xlabel '" + I18nManager.getText("fieldname.time") + " (" + I18nManager.getText("units.hours") + ")'\n");
-               }
-
-               // set other labels and plot chart
-               String chartTitle = null;
-               switch (inYaxis)
-               {
-               case 0: // y axis is distance
-                       inWriter.write("set ylabel '" + I18nManager.getText("fieldname.distance") + " (" + distLabel + ")'\n");
-                       chartTitle = I18nManager.getText("fieldname.distance");
-                       break;
-               case 1: // y axis is altitude
-                       inWriter.write("set ylabel '" + I18nManager.getText("fieldname.altitude") + " (" + altLabel + ")'\n");
-                       chartTitle = I18nManager.getText("fieldname.altitude");
-                       break;
-               case 2: // y axis is speed
-                       inWriter.write("set ylabel '" + I18nManager.getText("fieldname.speed") + " (" + speedLabel + ")'\n");
-                       chartTitle = I18nManager.getText("fieldname.speed");
-                       break;
-               case 3: // y axis is vertical speed
-                       inWriter.write("set ylabel '" + I18nManager.getText("fieldname.verticalspeed") + " (" + vertSpeedLabel + ")'\n");
-                       chartTitle = I18nManager.getText("fieldname.verticalspeed");
-                       break;
-               }
-               inWriter.write("set style fill solid 0.5 border -1\n");
-               inWriter.write("plot '" + tempFile.getAbsolutePath() + "' title '" + chartTitle + "' with filledcurve y1=0 lt rgb \"#009000\"\n");
-       }
-
-
-       /**
-        * Calculate the distance values for each point in the given track
-        * @param inTrack track object
-        * @return distance values in a ChartSeries object
-        */
-       private static ChartSeries getDistanceValues(Track inTrack)
-       {
-               // Calculate distances and fill in in values array
-               ChartSeries values = new ChartSeries(inTrack.getNumPoints());
-               double totalRads = 0;
-               DataPoint prevPoint = null, currPoint = null;
-               for (int i=0; i<inTrack.getNumPoints(); i++)
-               {
-                       currPoint = inTrack.getPoint(i);
-                       if (prevPoint != null && !currPoint.isWaypoint() && !currPoint.getSegmentStart())
-                       {
-                               totalRads += DataPoint.calculateRadiansBetween(prevPoint, currPoint);
-                       }
-
-                       // distance values use currently configured units
-                       values.setData(i, Distance.convertRadiansToDistance(totalRads));
-
-                       prevPoint = currPoint;
-               }
-               return values;
-       }
-
-       /**
-        * Calculate the time values for each point in the given track
-        * @param inTrack track object
-        * @return time values in a ChartSeries object
-        */
-       private static ChartSeries getTimeValues(Track inTrack)
-       {
-               // Calculate times and fill in in values array
-               ChartSeries values = new ChartSeries(inTrack.getNumPoints());
-               double seconds = 0.0;
-               Timestamp prevTimestamp = null;
-               DataPoint currPoint = null;
-               for (int i=0; i<inTrack.getNumPoints(); i++)
-               {
-                       currPoint = inTrack.getPoint(i);
-                       if (currPoint.hasTimestamp())
-                       {
-                               if (!currPoint.getSegmentStart() && prevTimestamp != null) {
-                                       seconds += (currPoint.getTimestamp().getMillisecondsSince(prevTimestamp) / 1000.0);
-                               }
-                               values.setData(i, seconds / 60.0 / 60.0);
-                               prevTimestamp = currPoint.getTimestamp();
-                       }
-               }
-               return values;
-       }
-
-       /**
-        * Calculate the altitude values for each point in the given track
-        * @param inTrack track object
-        * @return altitude values in a ChartSeries object
-        */
-       private static ChartSeries getAltitudeValues(Track inTrack)
-       {
-               ChartSeries values = new ChartSeries(inTrack.getNumPoints());
-               final double multFactor = Config.getUnitSet().getAltitudeUnit().getMultFactorFromStd();
-               for (int i=0; i<inTrack.getNumPoints(); i++) {
-                       if (inTrack.getPoint(i).hasAltitude()) {
-                               values.setData(i, inTrack.getPoint(i).getAltitude().getMetricValue() * multFactor);
-                       }
-               }
-               return values;
-       }
-
-       /**
-        * Calculate the speed values for each point in the given track
-        * @param inTrack track object
-        * @return speed values in a ChartSeries object
-        */
-       private static ChartSeries getSpeedValues(Track inTrack)
-       {
-               // Calculate speeds using the same formula as the profile chart
-               SpeedData speeds = new SpeedData(inTrack);
-               speeds.init(Config.getUnitSet());
-
-               final int numPoints = inTrack.getNumPoints();
-               ChartSeries values = new ChartSeries(numPoints);
-               // Loop over collected points
-               for (int i=0; i<numPoints; i++)
-               {
-                       if (speeds.hasData(i))
-                       {
-                               values.setData(i, speeds.getData(i));
-                       }
-               }
-               return values;
-       }
-
-       /**
-        * Calculate the vertical speed values for each point in the given track
-        * @param inTrack track object
-        * @return vertical speed values in a ChartSeries object
-        */
-       private static ChartSeries getVertSpeedValues(Track inTrack)
-       {
-               // Calculate speeds using the same formula as the profile chart
-               VerticalSpeedData speeds = new VerticalSpeedData(inTrack);
-               speeds.init(Config.getUnitSet());
-
-               final int numPoints = inTrack.getNumPoints();
-               ChartSeries values = new ChartSeries(numPoints);
-               // Loop over collected points
-               for (int i=0; i<numPoints; i++)
-               {
-                       if (speeds.hasData(i))
-                       {
-                               values.setData(i, speeds.getData(i));
-                       }
-               }
-               return values;
-       }
-
-
-       /**
-        * Select a file to write for the SVG output
-        * @return selected File object or null if cancelled
-        */
-       private File selectSvgFile()
-       {
-               if (_fileChooser == null)
-               {
-                       _fileChooser = new JFileChooser();
-                       _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
-                       _fileChooser.setFileFilter(new GenericFileFilter("filetype.svg", new String[] {"svg"}));
-                       _fileChooser.setAcceptAllFileFilterUsed(false);
-                       // start from directory in config which should be set
-                       String configDir = Config.getConfigString(Config.KEY_TRACK_DIR);
-                       if (configDir != null) {_fileChooser.setCurrentDirectory(new File(configDir));}
-               }
-               boolean chooseAgain = true;
-               while (chooseAgain)
-               {
-                       chooseAgain = false;
-                       if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
-                       {
-                               // OK pressed and file chosen
-                               File file = _fileChooser.getSelectedFile();
-                               // Check file extension
-                               if (!file.getName().toLowerCase().endsWith(".svg")) {
-                                       file = new File(file.getAbsolutePath() + ".svg");
-                               }
-                               // Check if file exists and if necessary prompt for overwrite
-                               Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
-                               if (!file.exists() || (file.canWrite() && JOptionPane.showOptionDialog(_parentFrame,
-                                               I18nManager.getText("dialog.save.overwrite.text"),
-                                               I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
-                                               JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
-                                       == JOptionPane.YES_OPTION))
-                               {
-                                       return file;
-                               }
-                               chooseAgain = true;
-                       }
-               }
-               // Cancel pressed so no file selected
-               return null;
-       }
-
-
-       /**
-        * @param inNumCharts number of charts to draw
-        * @return array of ints describing position and height of each subchart
-        */
-       private static int[] getHeights(int inNumCharts)
-       {
-               if (inNumCharts <= 1) {return new int[] {0, 100};}
-               if (inNumCharts == 2) {return new int[] {25, 75, 0, 25};}
-               if (inNumCharts == 3) {return new int[] {40, 60, 20, 20, 0, 20};}
-               return new int[] {54, 46, 36, 18, 18, 18, 0, 18};
-       }
-}