package com.aerodynelabs.habtk.prediction;
import java.awt.geom.Point2D;
import java.io.File;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.aerodynelabs.map.MapPath;
import com.aerodynelabs.map.MapPoint;
public abstract class Predictor implements Cloneable {
/**
* Valid predictor types
*/
private static Object[] options = {"Latex v1.0"};
/**
* Select which type of predictor.
* @return
*/
private static String select() {
String val = (String)JOptionPane.showInputDialog(
null, "Choose a prediction algorithm:\n", "New Flight Type",
JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
return val;
}
/**
* Create a new predictor.
* @return
*/
public static Predictor create() {
Predictor flight = null;
String type = select();
if(type == null) return null;
if(type.equals(options[0])) {
flight = new LatexPredictor();
}
if(flight.setup()) {
return flight;
} else {
return null;
}
}
/**
* Display a dialog to setup the predictor.
* @return
*/
public abstract boolean setup();
/**
* Load predictor from XML file chose by user.
* @return
*/
public static Predictor load() {
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("flights/"));
FileFilter filter = new FileNameExtensionFilter("Flight Definitions", "xml");
chooser.setFileFilter(filter);
File file;
int val = chooser.showOpenDialog(null);
if(val == JFileChooser.APPROVE_OPTION) {
file = chooser.getSelectedFile();
} else {
return null;
}
return load(file);
}
/**
* Load predictor from specified XML file.
* @param file
* @return
*/
public static Predictor load(File file) {
Predictor flight = null;
try {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(file);
doc.getDocumentElement().normalize();
Element root = doc.getDocumentElement();
if(root.getNodeName().equals("balloonFlight") == false) return null;
if(root.getElementsByTagName("predictor").item(0).getTextContent().equals("Latex Predictor v1.0")) {
flight = new LatexPredictor();
}
if(flight.read(doc) == false) return null;
} catch(Exception e) {
e.printStackTrace();
return null;
}
return flight;
}
/**
* Save predictor to XML file chose by user.
* @return
*/
public boolean save() {
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("flights/"));
FileFilter filter = new FileNameExtensionFilter("Flight Definitions", "xml");
chooser.setFileFilter(filter);
File file;
int val = chooser.showSaveDialog(null);
if(val == JFileChooser.APPROVE_OPTION) {
file = chooser.getSelectedFile();
String name = file.getName();
if(name.lastIndexOf('.') == -1) {
name += ".xml";
file = new File(file.getParentFile(), name);
}
} else {
return false;
}
return save(file);
}
/**
* Save predictor to given file.
* @param file
* @return
*/
public boolean save(File file) {
try {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.newDocument();
write(doc);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
DOMSource src = new DOMSource(doc);
StreamResult result = new StreamResult(file);
transformer.transform(src, result);
} catch(Exception e) {
e.printStackTrace();
}
return true;
}
/**
* Close the predictor.
*/
@Override
public abstract Predictor clone();
/**
* Write predictor into given XML document.
* @param doc
*/
public abstract void write(Document doc);
/**
* Read predictor from given XML document.
* @param doc
* @return
*/
public abstract boolean read(Document doc);
/**
* Run prediction from current start point to ground level.
* @return
*/
public abstract MapPath runPrediction();
/**
* Set if prediction should start with ascent.
* @param ascending
*/
public abstract void setAscending(boolean ascending);
/**
* Set termination altitude.
* @param level Altitude in meters above sea level.
*/
public abstract void setGroundLevel(double level);
/**
* Set start point.
* @param start
*/
public abstract void setStart(MapPoint start);
/**
* Set start time (seconds).
* @param time
*/
public abstract void setStartTime(long time);
/**
* Get the current start point.
* @return
*/
public abstract MapPoint getStart();
/**
* Get a name representing the type of this prediction.
* @return
*/
public abstract String getTypeName();
/**
* Get the lift or relevant field.
* @return
*/
public abstract double getLift();
/**
* Test if this prediction equals the given object.
*/
public abstract boolean equals(Object o);
/**
* Get the hash code of this prediction.
*/
public abstract int hashCode();
/**
* Get final lat/lon from start lat/lon, bearing and distance.
* @param start
* @param bearing degrees from north
* @param range meters
* @return
*/
protected Point2D.Double directGeodesic(Point2D.Double start, double bearing, double range) {
double radDist = range / 6367500;
double lat1 = Math.toRadians(start.y);
double lon1 = Math.toRadians(start.x);
double lat2 = Math.asin( Math.sin(lat1)*Math.cos(radDist) + Math.cos(lat1)*Math.sin(radDist)*Math.cos(bearing) );
double lon2 = lon1 + Math.atan2(Math.sin(bearing)*Math.sin(radDist)*Math.cos(lat1), Math.cos(radDist)-Math.sin(lat1)*Math.sin(lat2));
return new Point2D.Double(Math.toDegrees(lon2), Math.toDegrees(lat2));
}
public abstract MapPoint getBurst();
public abstract MapPoint getLanding();
}