package tim.prune.function;
import java.awt.BorderLayout;
import java.awt.Component;
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.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.NumberFormat;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import tim.prune.App;
import tim.prune.GenericFunction;
import tim.prune.I18nManager;
import tim.prune.data.DoubleRange;
/**
* Class to allow the download of OSM data (using the XAPI)
* for the area covered by the data
*/
public class DownloadOsmFunction extends GenericFunction implements Runnable
{
private JDialog _dialog = null;
private JLabel[] _latLonLabels = null;
private JProgressBar _progressBar = null;
private JButton _okButton = null;
private JFileChooser _fileChooser = null;
private File _selectedFile = null;
private boolean _cancelled = false;
/** Number formatter */
private final NumberFormat FORMAT_TWO_DP = NumberFormat.getNumberInstance();
/**
* Constructor
* @param inApp application object for callback
*/
public DownloadOsmFunction(App inApp)
{
super(inApp);
FORMAT_TWO_DP.setMaximumFractionDigits(2);
FORMAT_TWO_DP.setMinimumFractionDigits(2);
}
/** Get the name key */
public String getNameKey() {
return "function.downloadosm";
}
/**
* Begin the function
*/
public void begin()
{
// 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();
_fileChooser = new JFileChooser();
_fileChooser.setSelectedFile(new File("data.osm"));
}
initDialog();
_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(0, 10));
dialogPanel.add(new JLabel(I18nManager.getText("dialog.downloadosm.desc")), BorderLayout.NORTH);
// grid of labels to show lat/long extent
JPanel gridPanel = new JPanel();
gridPanel.setLayout(new GridLayout(3, 3));
_latLonLabels = new JLabel[4];
for (int i=0; i<4; i++) {
_latLonLabels[i] = new JLabel("0");
}
int lNum = 0;
for (int i=0; i<4; i++) {
gridPanel.add(new JLabel(" "));
gridPanel.add(_latLonLabels[lNum++]);
}
// layout main panel
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
mainPanel.add(gridPanel);
_progressBar = new JProgressBar();
_progressBar.setIndeterminate(true);
mainPanel.add(_progressBar);
dialogPanel.add(mainPanel, BorderLayout.CENTER);
// button panel at bottom
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
_okButton = new JButton(I18nManager.getText("button.ok"));
ActionListener okListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
finish();
}
};
_okButton.addActionListener(okListener);
buttonPanel.add(_okButton);
JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
_cancelled = true;
_dialog.dispose();
}
});
buttonPanel.add(cancelButton);
dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
return dialogPanel;
}
/**
* Initialise the values of the labels in the dialog
*/
private void initDialog()
{
// Get range of data
String[] lats = expandRange(_app.getTrackInfo().getTrack().getLatRange());
String[] lons = expandRange(_app.getTrackInfo().getTrack().getLonRange());
_latLonLabels[0].setText(lats[1]); // max lat
_latLonLabels[1].setText(lons[0]); // min lon
_latLonLabels[2].setText(lons[1]); // max lon
_latLonLabels[3].setText(lats[0]); // min lat
_okButton.setEnabled(true);
_progressBar.setVisible(false);
_cancelled = false;
}
/**
* Expand the given range to reasonable limits
* @param inRange range of lat/long values
* @return expanded range as pair of Strings
*/
private String[] expandRange(DoubleRange inRange)
{
double mid = (inRange.getMaximum() + inRange.getMinimum()) / 2.0;
double range = inRange.getRange();
double max = 0.0, min = 0.0;
// Expand range to at least 0.02 degree
if (range < 0.02)
{
min = mid - 0.01;
max = mid + 0.01;
}
else {
// expand by 10% in both directions
min = mid - range * 0.55;
max = mid + range * 0.55;
}
// Round min down to 0.01 degree
int minCents = (int) (100 * min);
// Round max upwards likewise
int maxCents = (int) (100 * max + 1);
final String[] answer = new String[] {FORMAT_TWO_DP.format(minCents/100.0),
FORMAT_TWO_DP.format(maxCents/100.0)};
return answer;
}
/**
* Finish the dialog when OK pressed
*/
private void finish()
{
if (!_okButton.isEnabled()) return;
_selectedFile = selectOsmFile();
if (_selectedFile != null)
{
// Show progress bar
_okButton.setEnabled(false);
_progressBar.setVisible(true);
new Thread(this).start();
}
else
_dialog.dispose();
}
/**
* Select a file to save the OSM data to
* @return selected file or null if cancelled
*/
private File selectOsmFile()
{
File saveFile = null;
boolean chooseAgain = false;
do
{
chooseAgain = false;
if (_fileChooser.showSaveDialog(_dialog) == JFileChooser.APPROVE_OPTION)
{
// OK pressed and file chosen
File file = _fileChooser.getSelectedFile();
// Check if file exists and if necessary prompt for overwrite
Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
if (!file.exists() || JOptionPane.showOptionDialog(_dialog,
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)
{
// new file or overwrite confirmed
saveFile = file;
}
else
{
// file exists and overwrite cancelled - select again
chooseAgain = true;
}
}
} while (chooseAgain);
return saveFile;
}
/**
* Do the actual download - launched to run in another thread
*/
public void run()
{
final String url = "http://www.overpass-api.de/api/xapi?map?bbox=" +
_latLonLabels[1].getText() + "," + _latLonLabels[3].getText() + "," +
_latLonLabels[2].getText() + "," + _latLonLabels[0].getText();
byte[] buffer = new byte[1024];
InputStream inStream = null;
FileOutputStream outStream = null;
int numBytesRead = 0;
try
{
inStream = new URL(url).openStream();
outStream = new FileOutputStream(_selectedFile);
// Loop and copy bytes to file
while ((numBytesRead = inStream.read(buffer)) > -1 && !_cancelled)
{
outStream.write(buffer, 0, numBytesRead);
}
}
catch (MalformedURLException mue) {}
catch (IOException ioe) {
_app.showErrorMessageNoLookup(getNameKey(), ioe.getClass().getName() + " - " + ioe.getMessage());
}
// clean up streams
finally {
try {inStream.close();} catch (Exception e) {}
try {outStream.close();} catch (Exception e) {}
}
// close dialog
_dialog.dispose();
}
}