package org.osm2world.viewer.control.actions;
import java.awt.Cursor;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import javax.swing.AbstractAction;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.osm2world.core.ConversionFacade.BoundingBoxSizeException;
import org.osm2world.core.ConversionFacade.Phase;
import org.osm2world.core.ConversionFacade.ProgressListener;
import org.osm2world.core.map_elevation.creation.EleConstraintEnforcer;
import org.osm2world.core.map_elevation.creation.TerrainInterpolator;
import org.osm2world.core.math.InvalidGeometryException;
import org.osm2world.core.osm.creation.OSMDataReader;
import org.osm2world.core.util.functions.DefaultFactory;
import org.osm2world.core.util.functions.Factory;
import org.osm2world.viewer.model.Data;
import org.osm2world.viewer.model.RenderOptions;
import org.osm2world.viewer.view.ProgressDialog;
import org.osm2world.viewer.view.ViewerFrame;
public abstract class AbstractLoadOSMAction extends AbstractAction {
protected ViewerFrame viewerFrame;
protected Data data;
protected RenderOptions renderOptions;
protected AbstractLoadOSMAction(String label, ViewerFrame viewerFrame, Data data, RenderOptions renderOptions) {
super(label);
this.viewerFrame = viewerFrame;
this.data = data;
this.renderOptions = renderOptions;
}
protected void loadOSMData(OSMDataReader dataReader, boolean resetCamera) {
LoadOSMThread thread = new LoadOSMThread(dataReader, resetCamera);
thread.setUncaughtExceptionHandler(
new ConversionExceptionHandler(viewerFrame));
thread.start();
}
private class LoadOSMThread extends Thread implements ProgressListener {
private final OSMDataReader dataReader;
private final boolean resetCamera;
private ProgressDialog progressDialog;
public LoadOSMThread(OSMDataReader dataReader, boolean resetCamera) {
super("OpenOSMThread");
this.dataReader = dataReader;
this.resetCamera = resetCamera;
}
@Override
public void run() {
viewerFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
viewerFrame.setEnabled(false);
boolean failOnLargeBBox = true;
boolean runAgain = true;
/* attempt to open the file */
while (runAgain) {
runAgain = false;
progressDialog = new ProgressDialog(viewerFrame, "Open OSM File");
try {
data.loadOSMData(dataReader, failOnLargeBBox,
new DefaultFactory<TerrainInterpolator>(
renderOptions.getInterpolatorClass()),
new DefaultFactory<EleConstraintEnforcer>(
renderOptions.getEnforcerClass()),
this);
if (resetCamera) {
new ResetCameraAction(viewerFrame, data, renderOptions).actionPerformed(null);
}
} catch (IOException e) {
JOptionPane.showMessageDialog(viewerFrame,
e.toString() + "\nCause: " +
(e.getCause() == null ? "unknown" : e.getCause()),
"Could not open OSM file", JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
} catch (InvalidGeometryException e) {
JOptionPane.showMessageDialog(viewerFrame,
"The OSM data contains broken geometry.\n"
+ "Make sure you are not using an extract with"
+ " incomplete areas!\nIf you are e.g. using Osmosis,"
+ " you need to use its completeWays=yes parameter.\n"
+ "See command line output for error details.",
"Could not perform conversion", JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
} catch (BoundingBoxSizeException e) {
String[] options = new String[] {"Try anyway" ,"Cancel"};
int answer = JOptionPane.showOptionDialog(viewerFrame,
"The input file covers a large bounding box.\n"
+ "This viewer can only handle relatively small areas well.\n",
"Large bounding box",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.WARNING_MESSAGE,
null, options, options[1]);
failOnLargeBBox = false;
runAgain = (answer != 1);
}
progressDialog.dispose();
}
viewerFrame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
viewerFrame.setEnabled(true);
}
@Override
public void updatePhase(Phase newPhase) {
switch (newPhase) {
case MAP_DATA:
progressDialog.setProgress(0);
progressDialog.setText("1/5: Organize information from .osm file...");
break;
case REPRESENTATION:
progressDialog.setProgress(20);
progressDialog.setText("2/5: Choose visual representations for OSM objects...");
break;
case ELEVATION:
progressDialog.setProgress(40);
progressDialog.setText("3/5: Guess elevations from available information...");
break;
case TERRAIN:
progressDialog.setProgress(60);
progressDialog.setText("4/5: Generate terrain...");
break;
case FINISHED:
progressDialog.setProgress(80);
progressDialog.setText("5/5: Represent objects by 3D primitives...");
break;
}
}
}
private static class ConversionExceptionHandler
implements UncaughtExceptionHandler {
private final ViewerFrame viewerFrame;
public ConversionExceptionHandler(ViewerFrame viewerFrame) {
this.viewerFrame = viewerFrame;
}
@Override
public void uncaughtException(final Thread t, final Throwable e) {
if (SwingUtilities.isEventDispatchThread()) {
showException(t, e);
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
showException(t, e);
}
});
}
}
private void showException(Thread t, Throwable e) {
// TODO log
e.printStackTrace();
String msg = String.format(
"Unexpected problem on thread %s:\n%s\n\n"
+ "OSM2World will be closed now.\n\nLocation:\n%s\n%s",
t.getName(), e.toString(),
e.getStackTrace()[0], e.getStackTrace()[1]);
JOptionPane.showMessageDialog(viewerFrame, msg,
"Error", JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
}
}