Merge branch 'master' of https://eng-git.canterbury.ac.nz/seng302-2017/team-7 into changeStartingPositions2

main
Erika Savell 9 years ago
commit 2d2f4692a9

@ -0,0 +1,3 @@
<component name="CopyrightManager">
<settings default="" />
</component>

@ -22,11 +22,11 @@ public class Constants {
public static final GPSCoordinate finishLineMarker2 = new GPSCoordinate(32.317257, -64.836260);
public static final BoatInRace[] OFFICIAL_AC35_COMPETITORS = new BoatInRace[]
{new BoatInRace("Oracle Team USA", 30.0, Color.BLUEVIOLET, "USA"),
{new BoatInRace("Oracle Team USA", 30.0, Color.BLUEVIOLET, "Oracle"),
new BoatInRace("Land Rover BAR", 23.0, Color.BLACK, "BAR"),
new BoatInRace("SoftBank Team Japan", 27.0, Color.RED, "JAP"),
new BoatInRace("Groupama Team France", 25.0, Color.ORANGE, "FRN"),
new BoatInRace("Artemis Racing", 22.5, Color.DARKOLIVEGREEN, "ART"),
new BoatInRace("Emirates Team New Zealand", 62, Color.LIMEGREEN, "ENZ")};
new BoatInRace("Emirates Team New Zealand", 62, Color.LIMEGREEN, "ETNZ")};
}

@ -2,21 +2,28 @@ package seng302.Controllers;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.layout.GridPane;
import javafx.util.Callback;
import org.xml.sax.SAXException;
import org.geotools.referencing.GeodeticCalculator;
import seng302.Constants;
import seng302.GPSCoordinate;
import seng302.Model.*;
import seng302.RaceXMLReader;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.awt.geom.Point2D;
import java.net.URL;
import java.util.ArrayList;
@ -25,20 +32,30 @@ import java.util.ResourceBundle;
/**
* Created by fwy13 on 15/03/2017.
*/
public class RaceController extends Controller {
ResizableRaceCanvas raceMap;
public class RaceController extends Controller{
@FXML
AnchorPane canvasBase;
ResizableRaceCanvas raceMap;
@FXML
GridPane startScreen;
@FXML
SplitPane ongoingRacePane;
@FXML
CheckBox showFPS;
@FXML
public CheckBox showAnno;
@FXML
Label timer;
@FXML
Label FPS;
@FXML
TableView<BoatInRace> boatInfoTable;
@FXML
@ -108,6 +125,7 @@ public class RaceController extends Controller {
}
/**
* Initializes and runs the race, based on the user's chosen scale factor
* Currently uses an example racecourse
@ -115,8 +133,22 @@ public class RaceController extends Controller {
* @param scaleFactor
*/
private void startRace(int scaleFactor) {
BoatInRace[] boats = generateAC35Competitors();
RaceXMLReader raceXMLReader = null;
try {
raceXMLReader = new RaceXMLReader("raceXML/bermuda_AC35.xml");
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
BoatInRace[] boats = new BoatInRace[raceXMLReader.getBoats().size()];
boats = raceXMLReader.getBoats().toArray(boats);
//BoatInRace[] boats = generateAC35Competitors();
raceMap = new ResizableRaceCanvas();
raceMap.setMouseTransparent(true);
raceMap.widthProperty().bind(canvasBase.widthProperty());
raceMap.heightProperty().bind(canvasBase.heightProperty());
raceMap.setBoats(boats);
@ -127,16 +159,35 @@ public class RaceController extends Controller {
startScreen.setVisible(false);
ongoingRacePane.setVisible(true);
ArrayList<Leg> legs = generateBermudaCourseLegs();
//ArrayList<Leg> legs = generateBermudaCourseLegs();
ArrayList<Leg> legs = raceXMLReader.getLegs();
ConstantVelocityRace race = new ConstantVelocityRace(boats, legs, this, scaleFactor);
showFPS.setVisible(true);
showFPS.selectedProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> ov,
Boolean old_val, Boolean new_val) {
if (showFPS.isSelected()){
FPS.setVisible(true);
} else {
FPS.setVisible(false);
}
}
});
showAnno.selectedProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> ov,
Boolean old_val, Boolean new_val) {
raceMap.toggleAnno();
}
});
(new Thread(race)).start();
new Thread((race)).start();
}
/**
* Generates an example race course (Bermuda 2017)
*
* Function for the Bermuda Race.
* @return legs in the Bermuda Race.
*/
private ArrayList<Leg> generateBermudaCourseLegs() {
@ -175,4 +226,7 @@ public class RaceController extends Controller {
timer.setText(time);
}
public void setFrames(String fps) { FPS.setText((fps)); }
}

@ -21,7 +21,7 @@ public class Boat {
*/
public Boat(String name, double velocity, String abbrev) {
this.velocity = velocity;
this.velocityProp = new SimpleStringProperty(String.valueOf(velocity));
this.velocityProp = new SimpleStringProperty(String.valueOf(velocity* 1.94384));
this.abbrev = abbrev;
this.name = new SimpleStringProperty(name);
}
@ -54,7 +54,7 @@ public class Boat {
* @return Name of the boat.
*/
public String toString() {
return getName().toString();
return getName().getValue();
}
/**

@ -1,8 +1,11 @@
package seng302.Model;
import javafx.animation.AnimationTimer;
import javafx.application.Platform;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableArray;
import javafx.collections.ObservableList;
import org.geotools.referencing.GeodeticCalculator;
import seng302.Controllers.RaceController;
@ -27,12 +30,11 @@ public abstract class Race implements Runnable {
protected int scaleFactor;
private int SLEEP_TIME = 100; //time in milliseconds to pause in a paced loop
protected int PRERACE_TIME = 100;//Integer.MAX_VALUE; //time in milliseconds to pause during pre-race
protected int PRERACE_TIME = 10000;//Integer.MAX_VALUE; //time in milliseconds to pause during pre-race
private boolean timerEnabled = true;
/**
* Initailiser for Race
*
* @param boats Takes in an array of boats that are participating in the race.
* @param legs Number of marks in order that the boats pass in order to complete the race.
*/
@ -50,7 +52,6 @@ public abstract class Race implements Runnable {
/**
* Constructor for Race class
*
* @param boats boats participating in the race.
* @param legs legs that there are in the race.
*/
@ -90,33 +91,21 @@ public abstract class Race implements Runnable {
*/
public void run() {
setControllerListeners();
preRace();
if (timerEnabled) countdownTimer();
initialiseBoats();
if(timerEnabled) countdownTimer();
simulateRace();
}
public void disableTimer() {
timerEnabled = false;
}
/**
* Initialises the boats,
* Sets the boats' current to the first leg in the race
* Disable the timer
*/
private void preRace() {
//show the boats participating.
ArrayList<GPSCoordinate> startPositons = getSpreadStartingPositions();
for (int i = 0; i < startingBoats.size(); i++) {
if (startingBoats.get(i) != null) {
startingBoats.get(i).setCurrentLeg(legs.get(0));
}
}
public void disableTimer() {
timerEnabled = false;
}
/**
* Prerace timer showing time until the race will begin, as a negative value
* Countdown timer until race starts. Use PRERACE_TIME to set countdown duration.
*/
protected void countdownTimer() {
long currentTime = System.currentTimeMillis();
@ -129,18 +118,15 @@ public abstract class Race implements Runnable {
long timeLoopEnded;
while (currentTime <= startTime) {
if (controller != null) controller.updateMap(startingBoats);
timeLeft = startTime - currentTime;
if (timeLeft == 0 && controller != null) {
updateTime("Race is starting...");
} else {
currentTimeInSeconds = timeLeft / 1000;
minutes = currentTimeInSeconds / 60;
remainingSeconds = currentTimeInSeconds % 60;
hours = minutes / 60;
minutes = minutes % 60;
if (controller != null) {
updateTime(String.format("Race clock: -%02d:%02d:%02d", hours, minutes, remainingSeconds));
}
updateTime(String.format("Time until race starts: %02d:%02d:%02d", hours, minutes, remainingSeconds));
}
try {
timeLoopEnded = System.currentTimeMillis();
@ -153,8 +139,8 @@ public abstract class Race implements Runnable {
}
/**
* Takes elapsed time in minutes and scales it, converts to hh:mm:ss format
* @return String formatted race time, scaled
* Takes total time elapsed and format to hour:minute:second
* @return Formatted time as string
*/
protected String calcTimer() {
long minutes;
@ -162,24 +148,24 @@ public abstract class Race implements Runnable {
long remainingSeconds;
long hours;
currentTimeInSeconds = totalTimeElapsed / 1000;
long scaledTimeInSeconds = currentTimeInSeconds * scaleFactor;
minutes = scaledTimeInSeconds / 60;
remainingSeconds = scaledTimeInSeconds % 60;
currentTimeInSeconds = (totalTimeElapsed / 1000) * scaleFactor;
minutes = currentTimeInSeconds / 60;
remainingSeconds = currentTimeInSeconds % 60;
hours = minutes / 60;
minutes = minutes % 60;
return String.format("Race clock: %02d:%02d:%02d", hours, minutes, remainingSeconds);
}
/**
* Updates the GUI race clock
* @param time
* Updates the calculated time to the timer label
* @param time The calculated time from calcTimer() method
*/
protected void updateTime(String time) {
protected void updateTime(String time){
Platform.runLater(() -> {controller.setTimer(time);});
}
Platform.runLater(() -> {
controller.setTimer(time);
});
private void updateFPS(int fps) {
Platform.runLater(() -> {controller.setFrames("FPS: " + fps);});
}
/**
@ -188,14 +174,24 @@ public abstract class Race implements Runnable {
*/
private void simulateRace() {
System.setProperty("javafx.animation.fullspeed", "true");
new AnimationTimer() {
long timeRaceStarted = System.currentTimeMillis();
long timeLoopStarted;
long timeLoopEnded;
int fps = 0;
long timeCurrent = System.currentTimeMillis();
while (boatsFinished < startingBoats.size()) {
timeLoopStarted = System.currentTimeMillis();
totalTimeElapsed = System.currentTimeMillis() - timeRaceStarted;
@Override
public void handle(long arg0) {
/*long timeLoopStarted;
long timeLoopEnded;
int fps = 0;*/
if (controller != null) controller.updateMap(startingBoats);
if (boatsFinished < startingBoats.size()) {
//timeLoopStarted = System.currentTimeMillis();
totalTimeElapsed = System.currentTimeMillis() - timeRaceStarted;
for (BoatInRace boat : startingBoats) {
if (boat != null && !boat.isFinished()) {
@ -204,26 +200,40 @@ public abstract class Race implements Runnable {
}
}
if (controller != null) controller.updateMap(startingBoats);
if (timerEnabled) updateTime(calcTimer());
//if (controller != null) controller.updateMap(startingBoats);
if (timerEnabled)
updateTime(calcTimer());
}
fps++;
if ((System.currentTimeMillis()-timeCurrent) > 1000){
updateFPS(fps);
fps = 0;
timeCurrent = System.currentTimeMillis();
}
;
/*fps++;
try {
timeLoopEnded = System.currentTimeMillis();
Thread.sleep(SLEEP_TIME - (timeLoopEnded - timeLoopStarted));
} catch (InterruptedException e) {
return;
}
}
}*/
};
//System.out.println("Avg fps:" + fps/(totalTimeElapsed/1000));
}.start();
}
/**
* Checks the position of the boat, this updates the boats current position.
*
* @param boat Boat that the postion is to be updated for.
* @param timeElapsed Time that has elapse since the start of the the race.
* @see BoatInRace
*/
protected void checkPosition(BoatInRace boat, long timeElapsed) {
if (boat.getDistanceTravelledInLeg() > boat.getCurrentLeg().getDistance()) {
if (boat.getDistanceTravelledInLeg() > boat.getCurrentLeg().getDistance()){
// updateController();
//boat has passed onto new leg
if (boat.getCurrentLeg().getName().equals("Finish")) {
//boat has finished
@ -231,16 +241,13 @@ public abstract class Race implements Runnable {
boat.setFinished(true);
boat.setTimeFinished(timeElapsed);
} else {
//Calculate how much the boat overshot the marker by
boat.setDistanceTravelledInLeg(boat.getDistanceTravelledInLeg() - boat.getCurrentLeg().getDistance());
//Move boat on to next leg
Leg nextLeg = legs.get(boat.getCurrentLeg().getLegNumber() + 1);
boat.setCurrentLeg(nextLeg);
//Add overshoot distance into the distance travelled for the next leg
boat.setDistanceTravelledInLeg(boat.getDistanceTravelledInLeg());
}
//Update the boat display table in the GUI to reflect the leg change
FXCollections.sort(startingBoats, (a, b) -> b.getCurrentLeg().getLegNumber() - a.getCurrentLeg().getLegNumber());
FXCollections.sort(startingBoats, (a,b) -> b.getCurrentLeg().getLegNumber() - a.getCurrentLeg().getLegNumber());
}
}
@ -248,12 +255,11 @@ public abstract class Race implements Runnable {
* Update call for the controller.
*/
protected void setControllerListeners() {
if (controller != null) controller.setInfoTable(this);
if(controller != null) controller.setInfoTable(this);
}
/**
* Returns the boats that have started the race.
*
* @return ObservableList of BoatInRace class that participated in the race.
* @see ObservableList
* @see BoatInRace
@ -262,12 +268,12 @@ public abstract class Race implements Runnable {
return startingBoats;
}
/**
* Updates the boat's gps coordinates depending on time elapsed
* @param boat
* @param millisecondsElapsed
* This function is a function that generates the Race and populates the events list.
* Is automatically called by the initialiser function, so that simulateRace() does not return an empty race.
* @see Race#simulateRace()
*/
protected abstract void updatePosition(BoatInRace boat, int millisecondsElapsed);
/**

@ -1,16 +1,23 @@
package seng302.Model;
import javafx.application.Platform;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.transform.Rotate;
import seng302.Constants;
import seng302.Controllers.RaceController;
import seng302.GPSCoordinate;
import seng302.GraphCoordinate;
import seng302.RaceMap;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Random;
/**
* This creates a JavaFX Canvas that is fills it's parent.
* Cannot be downsized.
@ -20,6 +27,8 @@ public class ResizableRaceCanvas extends Canvas {
private GraphicsContext gc;
private RaceMap map;
private BoatInRace[] boats;
private RaceController controller;
private boolean raceAnno = true;
/**
* Sets the boats that are to be displayed in this race.
@ -30,6 +39,7 @@ public class ResizableRaceCanvas extends Canvas {
this.boats = boats;
}
public ResizableRaceCanvas(RaceMap map) {
this.map = map;
gc = this.getGraphicsContext2D();
@ -56,7 +66,6 @@ public class ResizableRaceCanvas extends Canvas {
/**
* Displays the mark of a race as a circle.
*
* @param graphCoordinate Latitude and Logintude in GraphCoordinate that it is to be displayed as.
* @param paint Colour the mark is to be coloured.
* @see GraphCoordinate
@ -65,12 +74,11 @@ public class ResizableRaceCanvas extends Canvas {
*/
public void displayMark(GraphCoordinate graphCoordinate, Paint paint) {
gc.setFill(paint);
gc.fillOval(graphCoordinate.getX(), graphCoordinate.getY(), 15, 15);
gc.fillOval(graphCoordinate.getX(), graphCoordinate.getY(), 25, 25);
}
/**
* Displays a line on the map with rectangles on the starting and ending point of the line.
*
* @param graphCoordinateA Starting Point of the line in GraphCoordinate.
* @param graphCoordinateB End Point of the line in GraphCoordinate.
* @param paint Colour the line is to coloured.
@ -88,7 +96,6 @@ public class ResizableRaceCanvas extends Canvas {
/**
* Display a point on the Canvas
*
* @param graphCoordinate Coordinate that the point is to be displayed at.
* @param paint Colour that the boat is to be coloured.
* @see GraphCoordinate
@ -102,23 +109,22 @@ public class ResizableRaceCanvas extends Canvas {
/**
* Displays an arrow on the Canvas
*
* @param coordinate Coordinate that the arrow is to be displayed at.
* @param angle Angle that the arrow is to be facing in degrees 0 degrees = North (Up).
* @see GraphCoordinate
*/
public void displayArrow(GraphCoordinate coordinate, int angle) {
gc.save();
rotate(angle, coordinate.getX(), coordinate.getY());
gc.fillPolygon(new double[]{coordinate.getX() - 12, coordinate.getX() - 6, coordinate.getX(), coordinate.getX() - 4, coordinate.getX() - 4, coordinate.getX() - 8, coordinate.getX() - 8},
new double[]{coordinate.getY() - 5, coordinate.getY() - 20, coordinate.getY() - 5, coordinate.getY() - 5, coordinate.getY() + 20, coordinate.getY() + 20, coordinate.getY() - 5},
rotate(angle, coordinate.getX(),coordinate.getY());
gc.setFill(Color.BLACK);
gc.fillPolygon(new double[]{coordinate.getX()-12, coordinate.getX()-6, coordinate.getX(), coordinate.getX()-4, coordinate.getX()-4, coordinate.getX()-8, coordinate.getX()-8},
new double[]{coordinate.getY()-5, coordinate.getY()-20, coordinate.getY()-5, coordinate.getY()-5, coordinate.getY()+20, coordinate.getY()+20, coordinate.getY()-5},
7);
gc.restore();
}
/**
* Rotates things on the canvas Note: this must be called in between gc.save() and gc.restore() else they will rotate everything
*
* @param angle Bearing angle to rotate at in degrees
* @param px Pivot point x of rotation.
* @param py Pivot point y of rotation.
@ -130,14 +136,22 @@ public class ResizableRaceCanvas extends Canvas {
/**
* Display given name and speed of boat at a graph coordinate
*
* @param name name of the boat
* @param speed speed of the boat
* @param coordinate coordinate the text appears
*/
public void displayText(String name, double speed, GraphCoordinate coordinate) {
String text = name + ", " + speed + " knots";
gc.fillText(text, coordinate.getX() + 20, coordinate.getY());
public void displayText(String name, double speed, GraphCoordinate coordinate){
String text = String.format("%s, %2$.2f knots", name, speed);
//System.out.println(text.length()*7);
long xCoord = coordinate.getX()+20;
long yCoord = coordinate.getY();
if (xCoord+(text.length()*7) >= getWidth()){
xCoord -= text.length()*7;
}
if (yCoord-(text.length()*2) <= 0){
yCoord += 30;
}
gc.fillText(text, xCoord, yCoord);
}
/**
@ -151,7 +165,6 @@ public class ResizableRaceCanvas extends Canvas {
gc.clearRect(0, 0, width, height);
//System.out.println("Race Map Canvas Width: "+ width + ", Height:" + height);
this.map = new RaceMap(32.278, -64.863, 32.320989, -64.821, (int) width, (int) height);
if (map == null) {
return;
}
@ -181,8 +194,9 @@ public class ResizableRaceCanvas extends Canvas {
for (BoatInRace boat : boats) {
if (boat != null) {
// System.out.print("Drawing Boat At: " + boat.getCurrentPosition());
displayMark(this.map.convertGPS(boat.getCurrentPosition()), boat.getColour());
displayText(boat.getAbbrev(), boat.getVelocity(), this.map.convertGPS(boat.getCurrentPosition()));
displayPoint(this.map.convertGPS(boat.getCurrentPosition()), boat.getColour());
if (raceAnno){
displayText(boat.getAbbrev(), boat.getVelocity(), this.map.convertGPS(boat.getCurrentPosition()));}
}
}
}
@ -193,7 +207,6 @@ public class ResizableRaceCanvas extends Canvas {
/**
* Draws a boat at a certain GPSCoordinate
*
* @param colour Colour to colour boat.
* @param gpsCoordinates GPScoordinate that the boat is to be drawn at.
* @see GPSCoordinate
@ -205,6 +218,14 @@ public class ResizableRaceCanvas extends Canvas {
displayPoint(graphCoordinate, colour);
}
public void toggleAnno(){
if (raceAnno){
raceAnno = false;
} else {
raceAnno = true;
}
}
/**
* Set the Canvas to resizable.
*
@ -236,4 +257,5 @@ public class ResizableRaceCanvas extends Canvas {
public double prefHeight(double height) {
return getHeight();
}
}

@ -0,0 +1,165 @@
package seng302;
import javafx.scene.paint.Color;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import seng302.Model.Boat;
import seng302.Model.BoatInRace;
import seng302.Model.Leg;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.util.ArrayList;
/**
* Created by fwy13 on 26/03/2017.
*/
public class RaceXMLReader extends XMLReader{
private ArrayList<BoatInRace> boats = new ArrayList<>();
private Color[] colors = {Color.BLUEVIOLET, Color.BLACK, Color.RED, Color.ORANGE, Color.DARKOLIVEGREEN, Color.LIMEGREEN};//TODO make this established in xml or come up with a better system.
private ArrayList<Leg> legs = new ArrayList<>();
private GPSCoordinate mark, startPt1, startPt2, finishPt1, finishPt2, leewardPt1, leewardPt2, windwardPt1, windwardPt2;
private ArrayList<GPSCoordinate> boundary = new ArrayList<>();
public RaceXMLReader(String filePath) throws IOException, SAXException, ParserConfigurationException {
this(filePath, true);
}
public RaceXMLReader(String filePath, boolean read) throws IOException, SAXException, ParserConfigurationException {
super(filePath);
if (read) {
read();
}
}
private void read(){
readCourse();
readBoats();
readLegs();
}
public void readBoats(){
//get all boats
NodeList nBoats = doc.getElementsByTagName("boat");
for (int i = 0; i < nBoats.getLength(); i++){
String name = getTextValueOfNode((Element) nBoats.item(i), "name");
String abbrev = getTextValueOfNode((Element) nBoats.item(i), "abbr");
double velo = Double.parseDouble(getTextValueOfNode((Element) nBoats.item(i), "speed"));
//System.out.println(String.format("%s, %s, %s",name, abbrev, velo));
BoatInRace boat = new BoatInRace(name, velo, colors[i], abbrev);
boat.setCurrentPosition(startPt1);
boats.add(boat);
}
}
public void readLegs(){
//get all legs
NodeList nLegs = doc.getElementsByTagName("leg");
for (int i = 0; i < nLegs.getLength(); i++){
String label = getTextValueOfNode((Element) nLegs.item(i), "name");
NodeList start = ((Element) nLegs.item(i)).getElementsByTagName("start");
GPSCoordinate startCoord = getCoordinates(start);
NodeList finish = ((Element) nLegs.item(i)).getElementsByTagName("finish");
GPSCoordinate finishCoord = getCoordinates(finish);
//System.out.println(String.format("%s, %s, %s, %s",label, startCoord, finishCoord, i));
legs.add(new Leg(label, startCoord, finishCoord, i));
}
}
public void readCourse(){
NodeList nCourse = doc.getElementsByTagName("course");
NodeList nBounds = ((Element)nCourse.item(0)).getElementsByTagName("boundaries");
for (int i = 0; i < nBounds.getLength(); i++){
boundary.add(getCoordinates(nBounds, i));
}
NodeList nMarks = ((Element)nCourse.item(0)).getElementsByTagName("marker");
startPt1 = getCoordinates(nMarks, 0);
startPt2 = getCoordinates(nMarks, 0, 1);
mark = getCoordinates(nMarks, 1);
windwardPt1 = getCoordinates(nMarks, 2);
windwardPt2 = getCoordinates(nMarks, 2, 1);
leewardPt1 = getCoordinates(nMarks, 3);
leewardPt2 = getCoordinates(nMarks, 3, 1);
finishPt1 = getCoordinates(nMarks, 4);
finishPt2 = getCoordinates(nMarks, 4, 1);
}
private GPSCoordinate getCoordinates(NodeList start){
return getCoordinates(start, 0);
}
private GPSCoordinate getCoordinates(NodeList start, int startIndex){
return getCoordinates(start, startIndex, 0);
}
private GPSCoordinate getCoordinates(NodeList start, int startIndex, int nodeIndex){
NodeList nodeList = ((Element) start.item(startIndex)).getElementsByTagName("coordinate");
Element coord = (Element) nodeList.item(nodeIndex);
return getCoordinates(coord);
}
/**
* Returns the coordinate TODO raise exception that runs when the XML is formatted wrongly.
* @param coordNode
* @return
*/
private GPSCoordinate getCoordinates(Element coordNode){
double startLat = Double.parseDouble(getTextValueOfNode(coordNode, "latitude"));
double startLong = Double.parseDouble(getTextValueOfNode(coordNode, "longitude"));
return new GPSCoordinate(startLat, startLong);
}
public ArrayList<BoatInRace> getBoats() {
return boats;
}
public ArrayList<Leg> getLegs() {
return legs;
}
public GPSCoordinate getMark() {
return mark;
}
public GPSCoordinate getStartPt1() {
return startPt1;
}
public GPSCoordinate getStartPt2() {
return startPt2;
}
public GPSCoordinate getFinishPt1() {
return finishPt1;
}
public GPSCoordinate getFinishPt2() {
return finishPt2;
}
public GPSCoordinate getLeewardPt1() {
return leewardPt1;
}
public GPSCoordinate getLeewardPt2() {
return leewardPt2;
}
public GPSCoordinate getWindwardPt1() {
return windwardPt1;
}
public GPSCoordinate getWindwardPt2() {
return windwardPt2;
}
public ArrayList<GPSCoordinate> getBoundary() {
return boundary;
}
}

@ -0,0 +1,36 @@
package seng302;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import javax.xml.parsers.*;
import java.io.*;
/**
* Created by fwy13 on 26/03/2017.
*/
public abstract class XMLReader {
protected Document doc;
public XMLReader(String filePath) throws ParserConfigurationException, IOException, SAXException {
InputStream fXmlFile = getClass().getClassLoader().getResourceAsStream(filePath);
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
doc = dBuilder.parse(fXmlFile);
doc.getDocumentElement().normalize();
}
public Document getDocument(){
return doc;
}
public String getTextValueOfNode(Element n, String tagName) {
return n.getElementsByTagName(tagName).item(0).getTextContent();
}
public String getAttribute(Element n, String attr){
return n.getAttribute(attr);
}
}

@ -1,89 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="seng302.Controllers.RaceController">
<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.Controllers.RaceController">
<children>
<GridPane fx:id="startScreen" prefHeight="600.0" prefWidth="780.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="189.0" minWidth="10.0" prefWidth="93.0"/>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="372.0" minWidth="10.0" prefWidth="184.0"/>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="394.0" minWidth="10.0" prefWidth="192.0"/>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="416.0" minWidth="10.0" prefWidth="273.0"/>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="416.0" minWidth="10.0" prefWidth="57.0"/>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="189.0" minWidth="10.0" prefWidth="93.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="372.0" minWidth="10.0" prefWidth="184.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="394.0" minWidth="10.0" prefWidth="192.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="416.0" minWidth="10.0" prefWidth="273.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="416.0" minWidth="10.0" prefWidth="57.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="241.0" minHeight="10.0" prefHeight="102.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="383.0" minHeight="10.0" prefHeight="227.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="369.0" minHeight="10.0" prefHeight="59.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="369.0" minHeight="10.0" prefHeight="38.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="178.0" minHeight="10.0" prefHeight="178.0" vgrow="SOMETIMES"/>
<RowConstraints maxHeight="241.0" minHeight="10.0" prefHeight="102.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="383.0" minHeight="10.0" prefHeight="227.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="369.0" minHeight="10.0" prefHeight="59.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="369.0" minHeight="10.0" prefHeight="38.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="178.0" minHeight="10.0" prefHeight="178.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Select Your Race Scaling:" GridPane.columnIndex="1"
GridPane.rowIndex="1">
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Select Your Race Scaling:" GridPane.columnIndex="1" GridPane.rowIndex="1">
<font>
<Font size="23.0"/>
<Font size="23.0" />
</font>
</Text>
<Button mnemonicParsing="false" onAction="#startRace1Min" text="15x faster" GridPane.columnIndex="1"
GridPane.rowIndex="2"/>
<Button mnemonicParsing="false" onAction="#startRaceNoScaling" text="No scaling"
GridPane.columnIndex="3" GridPane.rowIndex="2"/>
<Button mnemonicParsing="false" onAction="#startRace5Min" text="3x faster" GridPane.columnIndex="2"
GridPane.rowIndex="2"/>
<Label alignment="CENTER" text="Race will take ~1 minute" GridPane.columnIndex="1"
GridPane.rowIndex="3">
<Button mnemonicParsing="false" onAction="#startRace1Min" text="15x faster" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Button mnemonicParsing="false" onAction="#startRaceNoScaling" text="No scaling" GridPane.columnIndex="3" GridPane.rowIndex="2" />
<Button mnemonicParsing="false" onAction="#startRace5Min" text="3x faster" GridPane.columnIndex="2" GridPane.rowIndex="2" />
<Label alignment="CENTER" text="Race will take ~1 minute" GridPane.columnIndex="1" GridPane.rowIndex="3">
<opaqueInsets>
<Insets/>
<Insets />
</opaqueInsets>
<font>
<Font size="10.0"/>
<Font size="10.0" />
</font>
</Label>
<Label alignment="CENTER" layoutX="99.0" layoutY="407.0" text="Race will take ~5 minutes"
GridPane.columnIndex="2" GridPane.rowIndex="3">
<Label alignment="CENTER" layoutX="99.0" layoutY="407.0" text="Race will take ~5 minutes" GridPane.columnIndex="2" GridPane.rowIndex="3">
<font>
<Font size="10.0"/>
<Font size="10.0" />
</font>
<opaqueInsets>
<Insets/>
<Insets />
</opaqueInsets>
</Label>
<Label alignment="CENTER" layoutX="279.0" layoutY="407.0" text="Race will take ~15 minutes"
GridPane.columnIndex="3" GridPane.rowIndex="3">
<Label alignment="CENTER" layoutX="279.0" layoutY="407.0" text="Race will take ~15 minutes" GridPane.columnIndex="3" GridPane.rowIndex="3">
<font>
<Font size="10.0"/>
<Font size="10.0" />
</font>
<opaqueInsets>
<Insets/>
<Insets />
</opaqueInsets>
</Label>
</children>
</GridPane>
<SplitPane fx:id="ongoingRacePane" dividerPositions="0.70" visible="false" AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<SplitPane fx:id="ongoingRacePane" dividerPositions="0.7" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
<AnchorPane fx:id="canvasBase" prefHeight="581.0" prefWidth="537.0">
<children>
<Label fx:id="timer" layoutX="45.0" layoutY="146.0" text="0:0" AnchorPane.bottomAnchor="0.0"
AnchorPane.rightAnchor="0.0"/>
<Label fx:id="FPS" text="FPS: 0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" />
<Label fx:id="timer" layoutX="45.0" layoutY="146.0" text="0:0" AnchorPane.bottomAnchor="0.0" AnchorPane.rightAnchor="0.0" />
<TitledPane fx:id="userControl" animated="false" text="User Control" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<CheckBox fx:id="showFPS" mnemonicParsing="false" selected="true" text="Show FPS" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0" />
<CheckBox fx:id="showAnno" mnemonicParsing="false" selected="true" text="Show Annotations" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="25.0" />
</children>
</AnchorPane>
</content>
</TitledPane>
</children>
</AnchorPane>
<AnchorPane layoutX="450.0" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="200.0"
GridPane.columnIndex="1">
<AnchorPane layoutX="450.0" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="200.0" GridPane.columnIndex="1">
<children>
<TableView fx:id="boatInfoTable" layoutX="-2.0" prefHeight="600.0" prefWidth="264.0"
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="-2.0"
AnchorPane.rightAnchor="-62.0" AnchorPane.topAnchor="0.0">
<TableView fx:id="boatInfoTable" layoutX="-2.0" prefHeight="600.0" prefWidth="264.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="-2.0" AnchorPane.rightAnchor="-62.0" AnchorPane.topAnchor="0.0">
<columns>
<TableColumn fx:id="boatPlacingColumn" prefWidth="50.0" text="Place"/>
<TableColumn fx:id="boatTeamColumn" prefWidth="100.0" text="Team"/>
<TableColumn fx:id="boatMarkColumn" prefWidth="130.0" text="Mark"/>
<TableColumn fx:id="boatSpeedColumn" prefWidth="75.0" text="Speed"/>
<TableColumn fx:id="boatPlacingColumn" prefWidth="50.0" text="Place" />
<TableColumn fx:id="boatTeamColumn" prefWidth="100.0" text="Team" />
<TableColumn fx:id="boatMarkColumn" prefWidth="130.0" text="Mark" />
<TableColumn fx:id="boatSpeedColumn" prefWidth="75.0" text="Speed" />
</columns>
</TableView>
</children>

@ -0,0 +1,66 @@
package seng302.Model;/**
* Created by Gondr on 26/03/2017.
*/
import static org.junit.Assert.*;
import org.junit.Test;
import org.xml.sax.SAXException;
import seng302.RaceXMLReader;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
public class RaceXMLTest {
RaceXMLReader raceXMLReader;
@Test
public void canFindFile(){
try {
RaceXMLReader raceXMLReader = new RaceXMLReader("raceXML/bermuda_AC35.xml", false);
} catch (Exception e) {
fail("Cannot find raceXML/bermuda_AC35.xml in the resources folder");
}
}
@Test
public void canReadBoats(){
try {
RaceXMLReader raceXMLReader = new RaceXMLReader("raceXML/bermuda_AC35.xml", false);
raceXMLReader.readBoats();
} catch (Exception e) {
fail("Boat Unreadable");
}
}
@Test
public void canReadLegs(){
try {
RaceXMLReader raceXMLReader = new RaceXMLReader("raceXML/bermuda_AC35.xml", false);
raceXMLReader.readLegs();
} catch (Exception e) {
fail("Legs Unreadable");
}
}
@Test
public void canReadCourse(){
try {
RaceXMLReader raceXMLReader = new RaceXMLReader("raceXML/bermuda_AC35.xml", false);
raceXMLReader.readCourse();
System.out.println(raceXMLReader.getStartPt1());
System.out.println(raceXMLReader.getStartPt2());
System.out.println(raceXMLReader.getMark());
System.out.println(raceXMLReader.getWindwardPt1());
System.out.println(raceXMLReader.getWindwardPt2());
System.out.println(raceXMLReader.getLeewardPt1());
System.out.println(raceXMLReader.getLeewardPt2());
System.out.println(raceXMLReader.getFinishPt1());
System.out.println(raceXMLReader.getFinishPt2());
} catch (Exception e) {
e.printStackTrace();
fail("Course Unreadable");
}
}
}
Loading…
Cancel
Save