diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 00000000..e7bedf33
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/src/main/java/seng302/Constants.java b/src/main/java/seng302/Constants.java
index fc198910..d1182698 100644
--- a/src/main/java/seng302/Constants.java
+++ b/src/main/java/seng302/Constants.java
@@ -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")};
}
diff --git a/src/main/java/seng302/Controllers/RaceController.java b/src/main/java/seng302/Controllers/RaceController.java
index f28d5abc..9699be76 100644
--- a/src/main/java/seng302/Controllers/RaceController.java
+++ b/src/main/java/seng302/Controllers/RaceController.java
@@ -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 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 legs = generateBermudaCourseLegs();
+ //ArrayList legs = generateBermudaCourseLegs();
+ ArrayList legs = raceXMLReader.getLegs();
ConstantVelocityRace race = new ConstantVelocityRace(boats, legs, this, scaleFactor);
+ showFPS.setVisible(true);
+ showFPS.selectedProperty().addListener(new ChangeListener() {
+ public void changed(ObservableValue extends Boolean> ov,
+ Boolean old_val, Boolean new_val) {
+ if (showFPS.isSelected()){
+ FPS.setVisible(true);
+ } else {
+ FPS.setVisible(false);
+ }
+ }
+ });
- (new Thread(race)).start();
+ showAnno.selectedProperty().addListener(new ChangeListener() {
+ public void changed(ObservableValue extends Boolean> ov,
+ Boolean old_val, Boolean new_val) {
+ raceMap.toggleAnno();
+ }
+ });
+
+ new Thread((race)).start();
}
+
/**
- * Generates an example race course (Bermuda 2017)
- *
+ * Function for the Bermuda Race.
* @return legs in the Bermuda Race.
*/
private ArrayList generateBermudaCourseLegs() {
@@ -175,4 +226,7 @@ public class RaceController extends Controller {
timer.setText(time);
}
+ public void setFrames(String fps) { FPS.setText((fps)); }
+
+
}
diff --git a/src/main/java/seng302/Model/Boat.java b/src/main/java/seng302/Model/Boat.java
index 3114ad07..371ba522 100644
--- a/src/main/java/seng302/Model/Boat.java
+++ b/src/main/java/seng302/Model/Boat.java
@@ -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();
}
/**
diff --git a/src/main/java/seng302/Model/Race.java b/src/main/java/seng302/Model/Race.java
index 0956f94c..482d1cbe 100644
--- a/src/main/java/seng302/Model/Race.java
+++ b/src/main/java/seng302/Model/Race.java
@@ -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,14 +30,13 @@ 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.
+ * @param legs Number of marks in order that the boats pass in order to complete the race.
*/
public Race(BoatInRace[] boats, ArrayList legs, RaceController controller, int scaleFactor) {
@@ -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 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));
- }
+ currentTimeInSeconds = timeLeft / 1000;
+ minutes = currentTimeInSeconds / 60;
+ remainingSeconds = currentTimeInSeconds % 60;
+ hours = minutes / 60;
+ minutes = minutes % 60;
+ if (controller != null) {
+ 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,42 +174,66 @@ public abstract class Race implements Runnable {
*/
private void simulateRace() {
- long timeRaceStarted = System.currentTimeMillis();
- long timeLoopStarted;
- long timeLoopEnded;
+ System.setProperty("javafx.animation.fullspeed", "true");
- while (boatsFinished < startingBoats.size()) {
- timeLoopStarted = System.currentTimeMillis();
- totalTimeElapsed = System.currentTimeMillis() - timeRaceStarted;
+ new AnimationTimer() {
+ long timeRaceStarted = System.currentTimeMillis();
+ int fps = 0;
+ long timeCurrent = System.currentTimeMillis();
- for (BoatInRace boat : startingBoats) {
- if (boat != null && !boat.isFinished()) {
- updatePosition(boat, SLEEP_TIME);
- checkPosition(boat, totalTimeElapsed);
- }
- }
+ @Override
+ public void handle(long arg0) {
- if (controller != null) controller.updateMap(startingBoats);
- if (timerEnabled) updateTime(calcTimer());
- try {
- timeLoopEnded = System.currentTimeMillis();
- Thread.sleep(SLEEP_TIME - (timeLoopEnded - timeLoopStarted));
- } catch (InterruptedException e) {
- return;
- }
- }
+ /*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()) {
+ updatePosition(boat, SLEEP_TIME);
+ checkPosition(boat, totalTimeElapsed);
+ }
+ }
+
+ //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 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);
/**
diff --git a/src/main/java/seng302/Model/ResizableRaceCanvas.java b/src/main/java/seng302/Model/ResizableRaceCanvas.java
index 46c19ca5..a8962d05 100644
--- a/src/main/java/seng302/Model/ResizableRaceCanvas.java
+++ b/src/main/java/seng302/Model/ResizableRaceCanvas.java
@@ -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,24 +66,22 @@ 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.
+ * @param paint Colour the mark is to be coloured.
* @see GraphCoordinate
* @see Color
* @see Paint
*/
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.
+ * @param paint Colour the line is to coloured.
* @see GraphCoordinate
* @see Color
* @see Paint
@@ -88,9 +96,8 @@ 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.
+ * @param paint Colour that the boat is to be coloured.
* @see GraphCoordinate
* @see Paint
* @see Color
@@ -102,26 +109,25 @@ 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).
+ * @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.
+ * @param px Pivot point x of rotation.
+ * @param py Pivot point y of rotation.
*/
private void rotate(double angle, double px, double py) {
Rotate r = new Rotate(angle, px, py);
@@ -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 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,8 +207,7 @@ public class ResizableRaceCanvas extends Canvas {
/**
* Draws a boat at a certain GPSCoordinate
- *
- * @param colour Colour to colour boat.
+ * @param colour Colour to colour boat.
* @param gpsCoordinates GPScoordinate that the boat is to be drawn at.
* @see GPSCoordinate
* @see Color
@@ -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();
}
+
}
diff --git a/src/main/java/seng302/RaceXMLReader.java b/src/main/java/seng302/RaceXMLReader.java
new file mode 100644
index 00000000..746e48f5
--- /dev/null
+++ b/src/main/java/seng302/RaceXMLReader.java
@@ -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 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 legs = new ArrayList<>();
+ private GPSCoordinate mark, startPt1, startPt2, finishPt1, finishPt2, leewardPt1, leewardPt2, windwardPt1, windwardPt2;
+ private ArrayList 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 getBoats() {
+ return boats;
+ }
+
+ public ArrayList 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 getBoundary() {
+ return boundary;
+ }
+}
diff --git a/src/main/java/seng302/XMLReader.java b/src/main/java/seng302/XMLReader.java
new file mode 100644
index 00000000..9f858a39
--- /dev/null
+++ b/src/main/java/seng302/XMLReader.java
@@ -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);
+ }
+
+}
diff --git a/src/main/resources/scenes/racepane.fxml b/src/main/resources/scenes/racepane.fxml
index 84b21769..46c4ee89 100644
--- a/src/main/resources/scenes/racepane.fxml
+++ b/src/main/resources/scenes/racepane.fxml
@@ -1,89 +1,90 @@
+
+
-
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
+
-
-
-
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
-
-
-
+
+
+
+
diff --git a/src/test/java/seng302/Model/RaceXMLTest.java b/src/test/java/seng302/Model/RaceXMLTest.java
new file mode 100644
index 00000000..55070ef0
--- /dev/null
+++ b/src/test/java/seng302/Model/RaceXMLTest.java
@@ -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");
+ }
+ }
+
+}
\ No newline at end of file