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

main
Erika Savell 9 years ago
commit fb409af23b

@ -32,7 +32,8 @@ public class App extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
primaryStage.minHeightProperty().setValue(600);
primaryStage.minWidthProperty().setValue(780);
//load the first container
try {
FXMLLoader loader = new FXMLLoader();

@ -21,12 +21,14 @@ public class Constants {
public static final GPSCoordinate finishLineMarker1 = new GPSCoordinate(32.317379, -64.839291);
public static final GPSCoordinate finishLineMarker2 = new GPSCoordinate(32.317257, -64.836260);
public static final double wakeScale = 10;
public static final BoatInRace[] OFFICIAL_AC35_COMPETITORS = new BoatInRace[]
{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("Land Rover BAR", 23.0, Color.BLACK, "BGR"),
new BoatInRace("SoftBank Team Japan", 27.0, Color.RED, "JPN"),
new BoatInRace("Groupama Team France", 25.0, Color.ORANGE, "FRA"),
new BoatInRace("Artemis Racing", 22.5, Color.DARKOLIVEGREEN, "SWE"),
new BoatInRace("Emirates Team New Zealand", 62, Color.LIMEGREEN, "ETNZ")};
}

@ -12,6 +12,7 @@ import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.layout.GridPane;
import javafx.util.Callback;
@ -34,7 +35,7 @@ import java.util.ResourceBundle;
*/
public class RaceController extends Controller{
@FXML
AnchorPane canvasBase;
GridPane canvasBase;
ResizableRaceCanvas raceMap;
@ -55,7 +56,6 @@ public class RaceController extends Controller{
@FXML
Label FPS;
@FXML
TableView<BoatInRace> boatInfoTable;
@FXML
@ -76,7 +76,7 @@ public class RaceController extends Controller{
public void updateMap(ObservableList<BoatInRace> boats) {
BoatInRace[] boatInRaces = new BoatInRace[boats.size()];
raceMap.setBoats(boats.toArray(boatInRaces));
raceMap.drawRaceMap();
raceMap.update();
}
/**
@ -122,15 +122,38 @@ public class RaceController extends Controller{
@Override
public void initialize(URL location, ResourceBundle resources) {
//listener for fps
/*Tooltip tp = new Tooltip("");
tp.install(timer, tp);*/
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);
}
}
});
/*timer.setOnMouseEntered(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
tp.show(timer, timer.getLayoutX()+timer.getWidth()+10, timer.getLayoutY()+timer.getHeight()-10);
}
});
timer.setOnMouseExited(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
tp.hide();
}
});*/
}
/**
* Initializes and runs the race, based on the user's chosen scale factor
* Currently uses an example racecourse
*
* @param scaleFactor
* @param scaleFactor scale value of race
*/
private void startRace(int scaleFactor) {
RaceXMLReader raceXMLReader = null;
@ -145,12 +168,18 @@ public class RaceController extends Controller{
}
BoatInRace[] boats = new BoatInRace[raceXMLReader.getBoats().size()];
boats = raceXMLReader.getBoats().toArray(boats);
raceMap = new ResizableRaceCanvas();
//BoatInRace[] boats = generateAC35Competitors();
double lat1 = raceXMLReader.getMapTopLeft().getLatitude();
double long1 = raceXMLReader.getMapTopLeft().getLongitude();
double lat2 = raceXMLReader.getMapBottomRight().getLatitude();
double long2 = raceXMLReader.getMapBottomRight().getLongitude();
raceMap = new ResizableRaceCanvas(lat1, long1, lat2, long2);
raceMap.setMouseTransparent(true);
raceMap.widthProperty().bind(canvasBase.widthProperty());
raceMap.heightProperty().bind(canvasBase.heightProperty());
raceMap.setBoats(boats);
raceMap.setRaceBoundaries(raceXMLReader.getBoundary());
raceMap.drawBoats();
raceMap.drawRaceMap();
raceMap.setVisible(true);
@ -161,9 +190,7 @@ public class RaceController extends Controller{
ArrayList<Leg> legs = raceXMLReader.getLegs();
ConstantVelocityRace race = new ConstantVelocityRace(boats, legs, this, scaleFactor);
new Thread((race)).start();
//listener for fps
showFPS.setVisible(true);
showFPS.selectedProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> ov,
Boolean old_val, Boolean new_val) {
@ -179,12 +206,15 @@ public class RaceController extends Controller{
showAnno.selectedProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> ov,
Boolean old_val, Boolean new_val) {
raceMap.toggleAnno();
raceMap.drawRaceMap();
raceMap.toggleAnno();
raceMap.update();
}
});
new Thread((race)).start();
}
/**
* Set the value for the race clock label
* @param time time that the label will be updated to
@ -199,5 +229,4 @@ public class RaceController extends Controller{
*/
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(Math.round(velocity)));
this.abbrev = abbrev;
this.name = new SimpleStringProperty(name);
}
@ -48,6 +48,14 @@ public class Boat {
return velocity;
}
/**
* Sets the speed of the boat in knots.
* @param velocity speed in knots
*/
public void setVelocity(double velocity) {
this.velocity = velocity;
this.velocityProp.setValue(String.valueOf(Math.round(velocity)));
}
/**
* Print method prints the name of the boat

@ -4,8 +4,10 @@ import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.paint.Color;
import org.geotools.referencing.GeodeticCalculator;
import seng302.Constants;
import seng302.GPSCoordinate;
import java.awt.geom.Point2D;
/**
* Boat in the Race extends Boat.
@ -21,6 +23,7 @@ public class BoatInRace extends Boat {
private Color colour;
private boolean finished = false;
private StringProperty currentLegName;
private boolean started = false;
/**
* Constructor method.
@ -76,6 +79,25 @@ public class BoatInRace extends Boat {
return calculateHeading(azimuth);
}
/**
* Returns the position of the end of the boat's wake, which is 180 degrees
* from the boat's heading, and whose length is proportional to the boat's
* speed.
* @return GPSCoordinate of wake endpoint.
*/
public GPSCoordinate getWake() {
double reverseHeading = calculateHeading() - 180;
double distance = Constants.wakeScale * getVelocity();
GeodeticCalculator calc = new GeodeticCalculator();
calc.setStartingGeographicPoint(
new Point2D.Double(getCurrentPosition().getLongitude(), getCurrentPosition().getLatitude())
);
calc.setDirection(reverseHeading, distance);
Point2D endpoint = calc.getDestinationGeographicPoint();
return new GPSCoordinate(endpoint.getY(), endpoint.getX());
}
/**
* @return Scaled velocity of the boat
*/
@ -203,4 +225,11 @@ public class BoatInRace extends Boat {
this.finished = bool;
}
public boolean isStarted() {
return started;
}
public void setStarted(boolean started) {
this.started = started;
}
}

@ -13,6 +13,7 @@ import seng302.GPSCoordinate;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Random;
/**
* Parent class for races
@ -30,8 +31,8 @@ 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 = 10000;//Integer.MAX_VALUE; //time in milliseconds to pause during pre-race
private boolean timerEnabled = true;
protected int PRERACE_TIME = 5000; //time in milliseconds to pause during pre-race
private boolean timerEnabled = true; //boolean to determine if timer is ran
/**
* Initailiser for Race
@ -105,6 +106,21 @@ public abstract class Race implements Runnable {
timerEnabled = false;
}
/**
* Set up the state in waiting for the race starts.
*/
private void preRace() {
//show the boats participating.
System.out.println("Boats Participating:");
System.out.println("====================");
for (int i = 0; i < startingBoats.size(); i++) {
if (startingBoats.get(i) != null) {
System.out.println(i + 1 + ". " + startingBoats.get(i).toString() + ", Speed: "
+ Math.round(startingBoats.get(i).getVelocity()) + "kn");
startingBoats.get(i).setCurrentLeg(legs.get(0));
}
}
}
/**
* Countdown timer until race starts. Use PRERACE_TIME to set countdown duration.
@ -120,15 +136,18 @@ public abstract class Race implements Runnable {
long timeLoopEnded;
while (currentTime <= startTime) {
//if (controller != null) controller.updateMap(startingBoats);
timeLeft = startTime - currentTime;
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));
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));
}
}
try {
timeLoopEnded = System.currentTimeMillis();
@ -150,7 +169,7 @@ public abstract class Race implements Runnable {
long remainingSeconds;
long hours;
currentTimeInSeconds = (totalTimeElapsed / 1000) * scaleFactor;
currentTimeInSeconds = (totalTimeElapsed * scaleFactor)/ 1000;
minutes = currentTimeInSeconds / 60;
remainingSeconds = currentTimeInSeconds % 60;
hours = minutes / 60;
@ -174,6 +193,11 @@ public abstract class Race implements Runnable {
Platform.runLater(() -> {controller.setFrames("FPS: " + fps);});
}
private boolean doNotFinish() {
Random rand = new Random();
return rand.nextInt(100) < 1;
}
/**
* Starts the Race Simulation, playing the race start to finish with the timescale.
* This prints the boats participating, the order that the events occur in time order, and the respective information of the events.
@ -182,11 +206,15 @@ public abstract class Race implements Runnable {
System.setProperty("javafx.animation.fullspeed", "true");
for (BoatInRace boat: startingBoats){
boat.setStarted(true);
}
new AnimationTimer() {
long timeRaceStarted = System.currentTimeMillis();
int fps = 0;
long timeCurrent = System.currentTimeMillis();
long timeRaceStarted = System.currentTimeMillis(); //start time of loop
int fps = 0; //init fps value
long timeCurrent = System.currentTimeMillis(); //current time
@Override
public void handle(long arg0) {
@ -208,6 +236,11 @@ public abstract class Race implements Runnable {
if (controller != null) controller.updateMap(startingBoats);
if (timerEnabled)
updateTime(calcTimer());
} else {
//Exit animation timer
updateTime(calcTimer());
updateFPS(0); //race ended so fps = 0
stop(); //exit animation timer
}
fps++;
if ((System.currentTimeMillis()-timeCurrent) > 1000){
@ -236,21 +269,28 @@ public abstract class Race implements Runnable {
*/
protected void checkPosition(BoatInRace boat, long timeElapsed) {
if (boat.getDistanceTravelledInLeg() > boat.getCurrentLeg().getDistance()){
// updateController();
//boat has passed onto new leg
if (boat.getCurrentLeg().getName().equals("Finish")) {
//boat has finished
boatsFinished++;
boat.setFinished(true);
boat.setTimeFinished(timeElapsed);
} else if(doNotFinish()) {
boatsFinished++;
boat.setFinished(true);
boat.setCurrentLeg(new Leg("DNF",-1));
} 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());
}
FXCollections.sort(startingBoats, (a,b) -> b.getCurrentLeg().getLegNumber() - a.getCurrentLeg().getLegNumber());
//Update the boat display table in the GUI to reflect the leg change
FXCollections.sort(startingBoats, (a, b) -> b.getCurrentLeg().getLegNumber() - a.getCurrentLeg().getLegNumber());
}
}
@ -272,9 +312,9 @@ public abstract class Race implements Runnable {
}
/**
* 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()
* Updates the boat's gps coordinates depending on time elapsed
* @param boat
* @param millisecondsElapsed
*/
protected abstract void updatePosition(BoatInRace boat, int millisecondsElapsed);

@ -1,7 +1,9 @@
package seng302.Model;
import com.sun.corba.se.impl.orbutil.graph.Graph;
import javafx.application.Platform;
import javafx.beans.property.StringProperty;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
@ -17,6 +19,7 @@ import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
/**
* This creates a JavaFX Canvas that is fills it's parent.
@ -27,7 +30,10 @@ public class ResizableRaceCanvas extends Canvas {
private GraphicsContext gc;
private RaceMap map;
private BoatInRace[] boats;
private RaceController controller;
private boolean raceAnno = true;
private ArrayList<GPSCoordinate> raceBoundaries;
double[] xpoints = {}, ypoints = {};
/**
* Sets the boats that are to be displayed in this race.
@ -38,8 +44,8 @@ public class ResizableRaceCanvas extends Canvas {
this.boats = boats;
}
public ResizableRaceCanvas(RaceMap map) {
super();
this.map = map;
gc = this.getGraphicsContext2D();
// Redraw canvas when size changes.
@ -52,6 +58,12 @@ public class ResizableRaceCanvas extends Canvas {
*/
public ResizableRaceCanvas() {
this(null);
setMap(new RaceMap(32.278, -64.863, 32.320989, -64.821, (int) getWidth(), (int) getHeight()));
}
public ResizableRaceCanvas(double lat1, double long1, double lat2, double long2){
this(null);
setMap(new RaceMap(lat1, long1, lat2, long2, (int) getWidth(), (int) getHeight()));
}
/**
@ -71,9 +83,24 @@ public class ResizableRaceCanvas extends Canvas {
* @see Color
* @see Paint
*/
public void displayMark(GraphCoordinate graphCoordinate, Paint paint) {
public void displayMark(GraphCoordinate graphCoordinate, Paint paint){
double d = 25;
gc.setFill(paint);
gc.fillOval(graphCoordinate.getX() - (d/2), graphCoordinate.getY() - (d/2), d, d);
}
public void displayBoat(BoatInRace boat, double angle) {
GraphCoordinate pos = this.map.convertGPS(boat.getCurrentPosition());
Paint paint = boat.getColour();
double[] x = {pos.getX() - 6, pos.getX(), pos.getX() + 6};
double[] y = {pos.getY() + 12, pos.getY() - 12, pos.getY() + 12};
gc.setFill(paint);
gc.fillOval(graphCoordinate.getX(), graphCoordinate.getY(), 25, 25);
gc.save();
rotate(angle, pos.getX(), pos.getY());
gc.fillPolygon(x, y, 3);
gc.restore();
}
/**
@ -85,7 +112,7 @@ public class ResizableRaceCanvas extends Canvas {
* @see Color
* @see Paint
*/
public void displayLine(GraphCoordinate graphCoordinateA, GraphCoordinate graphCoordinateB, Paint paint) {
private void displayLine(GraphCoordinate graphCoordinateA, GraphCoordinate graphCoordinateB, Paint paint) {
gc.setStroke(paint);
gc.setFill(paint);
gc.fillOval(graphCoordinateA.getX() - 3, graphCoordinateA.getY() - 3, 6, 6);
@ -101,23 +128,24 @@ public class ResizableRaceCanvas extends Canvas {
* @see Paint
* @see Color
*/
public void displayPoint(GraphCoordinate graphCoordinate, Paint paint) {
private void displayPoint(GraphCoordinate graphCoordinate, Paint paint) {
gc.setFill(paint);
gc.fillOval(graphCoordinate.getX(), graphCoordinate.getY(), 10, 10);
}
/**
* 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) {
private void displayArrow(GraphCoordinate coordinate, int angle) {
gc.save();
rotate(angle, coordinate.getX(),coordinate.getY());
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},
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();
}
@ -139,8 +167,8 @@ public class ResizableRaceCanvas extends Canvas {
* @param speed speed of the boat
* @param coordinate coordinate the text appears
*/
public void displayText(String name, double speed, GraphCoordinate coordinate){
String text = String.format("%s, %2$.2f knots", name, speed);
private void displayText(String name, double speed, GraphCoordinate coordinate){
String text = String.format("%s, %2$.2fkn", name, speed);
//System.out.println(text.length()*7);
long xCoord = coordinate.getX()+20;
long yCoord = coordinate.getY();
@ -153,6 +181,23 @@ public class ResizableRaceCanvas extends Canvas {
gc.fillText(text, xCoord, yCoord);
}
/**
* Draws race map with up to date data.
*/
public void update() {
this.drawRaceMap();
this.updateBoats();
}
public void drawBoundaries(){
if (this.raceBoundaries == null){
return;
}
gc.setFill(Color.AQUA);
setRaceBoundCoordinates();
gc.fillPolygon(xpoints, ypoints, xpoints.length);
}
/**
* Draws the Race Map
*/
@ -163,13 +208,15 @@ 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;
return;//TODO this should return a exception in the future
}
this.map.setHeight((int)height);
this.map.setWidth((int)width);
//finish line
gc.setLineWidth(2);
drawBoundaries();
GraphCoordinate finishLineCoord1 = this.map.convertGPS(Constants.finishLineMarker1);
GraphCoordinate finishLineCoord2 = this.map.convertGPS(Constants.finishLineMarker2);
displayLine(finishLineCoord1, finishLineCoord2, Color.DARKRED);
@ -188,20 +235,10 @@ public class ResizableRaceCanvas extends Canvas {
displayLine(startline1, startline2, Color.GREEN);
updateBoats();
if (boats != null) {
for (BoatInRace boat : boats) {
if (boat != null) {
// System.out.print("Drawing Boat At: " + boat.getCurrentPosition());
displayPoint(this.map.convertGPS(boat.getCurrentPosition()), boat.getColour());
if (raceAnno){
displayText(boat.getAbbrev(), boat.getVelocity(), this.map.convertGPS(boat.getCurrentPosition()));}
}
}
}
//display wind direction arrow - specify origin point and angle
displayArrow(new GraphCoordinate(500, 20), 100);
//display wind direction arrow - specify origin point and angle - angle now set to random angle
displayArrow(new GraphCoordinate((int)getWidth()-40, 40), 150);
}
/**
@ -228,6 +265,48 @@ public class ResizableRaceCanvas extends Canvas {
}
}
/**
* Draws boats while race in progress, when leg heading is set.
*/
public void updateBoats() {
if (boats != null) {
for (BoatInRace boat : boats) {
boolean finished = boat.getCurrentLeg().getName().equals("Finish") || boat.getCurrentLeg().getName().equals("DNF");
boolean isStart = boat.isStarted();
if (!finished && isStart) {
displayBoat(boat, boat.calculateHeading());
GraphCoordinate wakeFrom = this.map.convertGPS(boat.getCurrentPosition());
GraphCoordinate wakeTo = this.map.convertGPS(boat.getWake());
displayLine(wakeFrom, wakeTo, boat.getColour());
} else if (!isStart){
displayBoat(boat, boat.calculateHeading());
} else {
displayBoat(boat, 0);
}
if (raceAnno) displayText(boat.getAbbrev(), boat.getVelocity(), this.map.convertGPS(boat.getCurrentPosition()));
}
}
}
public void setRaceBoundaries(ArrayList<GPSCoordinate> boundaries) {
this.raceBoundaries = new ArrayList<>();
for (GPSCoordinate bound: boundaries){
raceBoundaries.add(bound);
}
setRaceBoundCoordinates();
}
public void setRaceBoundCoordinates(){
xpoints = new double[this.raceBoundaries.size()];
ypoints = new double[this.raceBoundaries.size()];
for (int i = 0; i < raceBoundaries.size(); i++){
GraphCoordinate coord = map.convertGPS(raceBoundaries.get(i));
xpoints[i] = coord.getX();
ypoints[i] = coord.getY();
}
}
/**
* Set the Canvas to resizable.
*
@ -260,4 +339,17 @@ public class ResizableRaceCanvas extends Canvas {
return getHeight();
}
/**
* Draws boats during race setup, when leg heading is not set.
*/
public void drawBoats() {
if (boats != null) {
for (BoatInRace boat : boats) {
if (boat != null) {
displayBoat(boat, 0);
if (raceAnno) displayText(boat.getAbbrev(), boat.getVelocity(), this.map.convertGPS(boat.getCurrentPosition()));
}
}
}
}
}

@ -35,8 +35,16 @@ public class RaceMap {
* @see GraphCoordinate
*/
public GraphCoordinate convertGPS(double lat, double lon) {
int difference = Math.abs(width - height);
int size = width;
if (width > height){
size = height;
return new GraphCoordinate((int) ((size * (lon - x1) / (x2 - x1)) + difference/2), (int) (size - (size * (lat - y1) / (y2 - y1))));
}else{
return new GraphCoordinate((int) (size * (lon - x1) / (x2 - x1)), (int) ((size - (size * (lat - y1) / (y2 - y1))) + difference/2));
}
return new GraphCoordinate((int) (width * (lon - x1) / (x2 - x1)), (int) (height - (height * (lat - y1) / (y2 - y1))));
//return new GraphCoordinate((int) (width * (lon - x1) / (x2 - x1)), (int) (height - (height * (lat - y1) / (y2 - y1))));
}
/**
@ -50,4 +58,12 @@ public class RaceMap {
public GraphCoordinate convertGPS(GPSCoordinate coordinate) {
return convertGPS(coordinate.getLatitude(), coordinate.getLongitude());
}
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
}

@ -20,7 +20,9 @@ public class RaceXMLReader extends XMLReader{
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 GPSCoordinate mapTopLeft, mapBottomRight;
private ArrayList<GPSCoordinate> boundary = new ArrayList<>();
private static double COORDINATEPADDING = 0.0005;
public RaceXMLReader(String filePath) throws IOException, SAXException, ParserConfigurationException {
@ -36,8 +38,8 @@ public class RaceXMLReader extends XMLReader{
private void read(){
readCourse();
readBoats();
readLegs();
readBoats();
}
public void readBoats(){
@ -51,6 +53,9 @@ public class RaceXMLReader extends XMLReader{
//System.out.println(String.format("%s, %s, %s",name, abbrev, velo));
BoatInRace boat = new BoatInRace(name, velo, colors[i], abbrev);
boat.setCurrentPosition(startPt1);
if (legs.size() > 0){
boat.setCurrentLeg(legs.get(0));
}
boats.add(boat);
}
}
@ -74,9 +79,61 @@ public class RaceXMLReader extends XMLReader{
NodeList nCourse = doc.getElementsByTagName("course");
NodeList nBounds = ((Element)nCourse.item(0)).getElementsByTagName("boundaries");
nBounds = ((Element) nBounds.item(0)).getElementsByTagName("coordinate");
int maxLatitudeIndex = 0;
double maxLatitude = -Double.MIN_VALUE;
int maxLongitudeIndex = 0;
double maxLongitude = -180;
int minLatitudeIndex = 0;
double minLatitude = Double.MAX_VALUE;
int minLongitudeIndex = 0;
double minLongitude = Double.MAX_VALUE;
for (int i = 0; i < nBounds.getLength(); i++){
boundary.add(getCoordinates(nBounds, i));
boundary.add(getCoordinates((Element) nBounds.item(i)));
if (boundary.get(i).getLatitude() > maxLatitude){
maxLatitudeIndex = i;
maxLatitude = boundary.get(i).getLatitude();
}
if (boundary.get(i).getLatitude() < minLatitude){
minLatitudeIndex = i;
minLatitude = boundary.get(i).getLatitude();
}
if (boundary.get(i).getLongitude() > maxLongitude){
maxLongitudeIndex = i;
maxLongitude = boundary.get(i).getLongitude();
}
if (boundary.get(i).getLongitude() < minLongitude){
minLongitudeIndex = i;
minLongitude = boundary.get(i).getLongitude();
}
}
/*System.out.println(nBounds.getLength());
System.out.println(maxLatitude);
System.out.println(minLatitude);
System.out.println(maxLongitude);
System.out.println(minLongitude);*/
double difference = 0;//this will hold the largest difference so we can make the map square.
double latitudeDiff = Math.abs(Math.abs(boundary.get(maxLatitudeIndex).getLatitude()) - Math.abs(boundary.get(minLatitudeIndex).getLatitude()));
double longitudeDiff = Math.abs(Math.abs(boundary.get(maxLongitudeIndex).getLongitude()) - Math.abs(boundary.get(minLongitudeIndex).getLongitude()));
if (latitudeDiff >= longitudeDiff){
difference = latitudeDiff - longitudeDiff;
maxLongitude += difference/2;
minLongitude -= difference/2;
}else{
difference = longitudeDiff - latitudeDiff;
maxLatitude += difference/2;
minLatitude -= difference/2;
}
maxLatitude += COORDINATEPADDING;
minLatitude -= COORDINATEPADDING;
maxLongitude += COORDINATEPADDING;
minLongitude -= COORDINATEPADDING;
//now create map boundaries
//top left canvas point is min logitude, max latitude
//bottom right of canvas point is min longitude, max latitude.
mapTopLeft = new GPSCoordinate(minLatitude, minLongitude);
mapBottomRight = new GPSCoordinate(maxLatitude, maxLongitude);
NodeList nMarks = ((Element)nCourse.item(0)).getElementsByTagName("marker");
startPt1 = getCoordinates(nMarks, 0);
@ -190,4 +247,12 @@ public class RaceXMLReader extends XMLReader{
public ArrayList<GPSCoordinate> getBoundary() {
return boundary;
}
public GPSCoordinate getMapTopLeft() {
return mapTopLeft;
}
public GPSCoordinate getMapBottomRight() {
return mapBottomRight;
}
}

@ -9,31 +9,31 @@
<boat>
<name>Land Rover BAR</name>
<speed>50</speed>
<abbr>BAR</abbr>
<abbr>GBR</abbr>
<colour>BLACK</colour>
</boat>
<boat>
<name>SoftBank Team Japan</name>
<speed>40</speed>
<abbr>JAP</abbr>
<abbr>JPN</abbr>
<colour>RED</colour>
</boat>
<boat>
<name>Groupama Team France</name>
<speed>35</speed>
<abbr>FRN</abbr>
<abbr>FRA</abbr>
<colour>ORANGE</colour>
</boat>
<boat>
<name>Artemis Racing</name>
<speed>44</speed>
<abbr>ART</abbr>
<abbr>SWE</abbr>
<colour>DARKOLIVEGREEN</colour>
</boat>
<boat>
<name>Emirates Team New Zealand</name>
<speed>62</speed>
<abbr>ENZ</abbr>
<abbr>NZL</abbr>
<colour>LIMEGREEN</colour>
</boat>
</boats>
@ -169,12 +169,48 @@
<course>
<boundaries>
<coordinate>
<latitude>32.278</latitude>
<longitude>-64.863</longitude>
<latitude>32.313922</latitude>
<longitude>-64.837168</longitude>
</coordinate>
<coordinate>
<latitude>32.30989</latitude>
<longitude>-64.821</longitude>
<latitude>32.317403</latitude>
<longitude>-64.838627</longitude>
</coordinate>
<coordinate>
<latitude>32.317911</latitude>
<longitude>-64.836996</longitude>
</coordinate>
<coordinate>
<latitude>32.317548</latitude>
<longitude>-64.835022</longitude>
</coordinate>
<coordinate>
<latitude>32.304273</latitude>
<longitude>-64.822834</longitude>
</coordinate>
<coordinate>
<latitude>32.279097</latitude>
<longitude>-64.841545</longitude>
</coordinate>
<coordinate>
<latitude>32.279604</latitude>
<longitude>-64.849871</longitude>
</coordinate>
<coordinate>
<latitude>32.289545</latitude>
<longitude>-64.854162</longitude>
</coordinate>
<coordinate>
<latitude>32.290198</latitude>
<longitude>-64.858711</longitude>
</coordinate>
<coordinate>
<latitude>32.297164</latitude>
<longitude>-64.856394</longitude>
</coordinate>
<coordinate>
<latitude>32.296148</latitude>
<longitude>-64.849184</longitude>
</coordinate>
</boundaries>
<marker>

@ -61,22 +61,40 @@
</GridPane>
<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="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>
<GridPane fx:id="canvasBase">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Pane prefHeight="200.0" prefWidth="400.0" GridPane.halignment="LEFT" GridPane.valignment="TOP">
<children>
<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="77.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 Annotation" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="30.0" />
</children>
</AnchorPane>
</content>
</TitledPane>
</children>
</Pane>
<Label fx:id="timer" layoutX="45.0" layoutY="146.0" text="0:0" AnchorPane.bottomAnchor="0.0" AnchorPane.rightAnchor="0.0" GridPane.halignment="RIGHT" GridPane.valignment="BOTTOM">
<font>
<Font name="System Bold" size="15.0" />
</font>
</Label>
<Label fx:id="FPS" text="FPS: 0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" GridPane.halignment="LEFT" GridPane.valignment="BOTTOM">
<font>
<Font name="System Bold" size="15.0" />
</font>
</Label>
</children>
</GridPane>
<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">

@ -1,6 +1,8 @@
package seng302.Model;
import javafx.scene.paint.Color;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import seng302.GPSCoordinate;
@ -113,4 +115,43 @@ public class BoatInRaceTest {
assertFalse(testBoat.isFinished());
}
@Test
public void getWakeAtProperHeading() throws Exception {
BoatInRace boat = new BoatInRace("Test", 1, Color.ALICEBLUE, "tt");
// Construct leg of 0 degrees
GPSCoordinate startPoint = new GPSCoordinate(0, 0);
GPSCoordinate endPoint = new GPSCoordinate(50, 0);
Leg leg0deg = new Leg("Start", startPoint, endPoint, 0);
boat.setCurrentLeg(leg0deg);
boat.setCurrentPosition(new GPSCoordinate(0,0));
assertEquals(0, boat.calculateHeading(), 1e-8);
// Construct leg from wake - heading should be 180 degrees
Leg leg180deg = new Leg("Start", startPoint, boat.getWake(), 0);
boat.setCurrentLeg(leg180deg);
assertEquals(180, boat.calculateHeading(), 1e-8);
}
@Test
public void getWakeProportionalToVelocity() throws Exception {
BoatInRace boat = new BoatInRace("Test", 10, Color.ALICEBLUE, "tt");
// Construct leg of 0 degrees at 0 N
GPSCoordinate startPoint = new GPSCoordinate(0, 0);
GPSCoordinate endPoint = new GPSCoordinate(50, 0);
Leg leg0deg = new Leg("Start", startPoint, endPoint, 0);
boat.setCurrentLeg(leg0deg);
boat.setCurrentPosition(new GPSCoordinate(0,0));
// Get latitude of endpoint of wake at 10 kn (longitude is 0)
double endpointAt10Kn = boat.getWake().getLatitude();
// Latitude of endpoint at 20 kn should be twice endpoint at 10 kn
boat.setVelocity(20);
assertEquals(2*endpointAt10Kn, boat.getWake().getLatitude(), 1e-8);
}
}
Loading…
Cancel
Save