mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 14:28:43 +00:00
Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
@@ -5,6 +5,7 @@ import javafx.fxml.FXMLLoader;
|
|||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import seng302.models.PolarTable;
|
||||||
import seng302.models.stream.StreamParser;
|
import seng302.models.stream.StreamParser;
|
||||||
import seng302.models.stream.StreamReceiver;
|
import seng302.models.stream.StreamReceiver;
|
||||||
import seng302.server.ServerThread;
|
import seng302.server.ServerThread;
|
||||||
@@ -13,6 +14,8 @@ public class App extends Application {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage primaryStage) throws Exception {
|
public void start(Stage primaryStage) throws Exception {
|
||||||
|
PolarTable.parsePolarFile(getClass().getResource("/config/acc_polars.csv").getFile());
|
||||||
|
|
||||||
Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml"));
|
Parent root = FXMLLoader.load(getClass().getResource("/views/MainView.fxml"));
|
||||||
primaryStage.setTitle("RaceVision");
|
primaryStage.setTitle("RaceVision");
|
||||||
primaryStage.setScene(new Scene(root, 1530, 960));
|
primaryStage.setScene(new Scene(root, 1530, 960));
|
||||||
@@ -27,6 +30,8 @@ public class App extends Application {
|
|||||||
System.exit(0);
|
System.exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
@@ -65,6 +70,7 @@ public class App extends Application {
|
|||||||
else{
|
else{
|
||||||
// sr = new StreamReceiver("localhost", 4949, "RaceStream");
|
// sr = new StreamReceiver("localhost", 4949, "RaceStream");
|
||||||
// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream");
|
// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream");
|
||||||
|
// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4942, "RaceStream");
|
||||||
sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream");
|
sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package seng302;
|
||||||
|
|
||||||
|
import javafx.geometry.Point2D;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Class for performing geometric calculations on the canvas
|
||||||
|
* Created by wmu16 on 24/05/17.
|
||||||
|
*/
|
||||||
|
public final class GeometryUtils {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the line function on two points of a line and a test point to test which side of the line that point is
|
||||||
|
* on. If the return value is
|
||||||
|
* return 1, then the point is on one side of the line,
|
||||||
|
* return -1 then the point is on the other side of the line
|
||||||
|
* return 0 then the point is exactly on the line.
|
||||||
|
* @param linePoint1 One point of the line
|
||||||
|
* @param linePoint2 Second point of the line
|
||||||
|
* @param testPoint The point to test with this line
|
||||||
|
* @return A return value indicating which side of the line the point is on
|
||||||
|
*/
|
||||||
|
public static Integer lineFunction(Point2D linePoint1, Point2D linePoint2, Point2D testPoint) {
|
||||||
|
|
||||||
|
Double x = testPoint.getX();
|
||||||
|
Double y = testPoint.getY();
|
||||||
|
Double x1 = linePoint1.getX();
|
||||||
|
Double y1 = linePoint1.getY();
|
||||||
|
Double x2 = linePoint2.getX();
|
||||||
|
Double y2 = linePoint2.getY();
|
||||||
|
|
||||||
|
Double result = (x - x1)*(y2 - y1) - (y - y1)*(x2 - x1); //Line function
|
||||||
|
|
||||||
|
if (result > 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if (result < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a point and a vector (angle and vector length) Will create a new point, that vector away from the origin
|
||||||
|
* point
|
||||||
|
* @param originPoint The point with which to use as the base for our vector addition
|
||||||
|
* @param angleInDeg (DEGREES) The angle at which our new point is being created (in degrees!)
|
||||||
|
* @param vectorLength The length out on this angle from the origin point to create the new point
|
||||||
|
* @return a Point2D
|
||||||
|
*/
|
||||||
|
public static Point2D makeArbitraryVectorPoint(Point2D originPoint, Double angleInDeg, Double vectorLength) {
|
||||||
|
|
||||||
|
Double endPointX = originPoint.getX() + vectorLength * Math.cos(Math.toRadians(angleInDeg));
|
||||||
|
Double endPointY = originPoint.getY() + vectorLength * Math.sin(Math.toRadians(angleInDeg));
|
||||||
|
|
||||||
|
return new Point2D(endPointX, endPointY);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -186,9 +186,9 @@ public class CanvasController {
|
|||||||
double[] yBoundaryPoints = new double[courseLimits.size()];
|
double[] yBoundaryPoints = new double[courseLimits.size()];
|
||||||
for (int i = 0; i < courseLimits.size() - 1; i++) {
|
for (int i = 0; i < courseLimits.size() - 1; i++) {
|
||||||
Limit thisPoint1 = courseLimits.get(i);
|
Limit thisPoint1 = courseLimits.get(i);
|
||||||
SingleMark thisMark1 = new SingleMark("", thisPoint1.getLat(), thisPoint1.getLng(), thisPoint1.getSeqID());
|
SingleMark thisMark1 = new SingleMark("", thisPoint1.getLat(), thisPoint1.getLng(), thisPoint1.getSeqID(), thisPoint1.getSeqID());
|
||||||
Limit thisPoint2 = courseLimits.get(i+1);
|
Limit thisPoint2 = courseLimits.get(i+1);
|
||||||
SingleMark thisMark2 = new SingleMark("", thisPoint2.getLat(), thisPoint2.getLng(), thisPoint2.getSeqID());
|
SingleMark thisMark2 = new SingleMark("", thisPoint2.getLat(), thisPoint2.getLng(), thisPoint2.getSeqID(), thisPoint2.getSeqID());
|
||||||
Point2D borderPoint1 = findScaledXY(thisMark1);
|
Point2D borderPoint1 = findScaledXY(thisMark1);
|
||||||
Point2D borderPoint2 = findScaledXY(thisMark2);
|
Point2D borderPoint2 = findScaledXY(thisMark2);
|
||||||
gc.strokeLine(borderPoint1.getX(), borderPoint1.getY(),
|
gc.strokeLine(borderPoint1.getX(), borderPoint1.getY(),
|
||||||
@@ -197,9 +197,9 @@ public class CanvasController {
|
|||||||
yBoundaryPoints[i] = borderPoint1.getY();
|
yBoundaryPoints[i] = borderPoint1.getY();
|
||||||
}
|
}
|
||||||
Limit thisPoint1 = courseLimits.get(courseLimits.size()-1);
|
Limit thisPoint1 = courseLimits.get(courseLimits.size()-1);
|
||||||
SingleMark thisMark1 = new SingleMark("", thisPoint1.getLat(), thisPoint1.getLng(), thisPoint1.getSeqID());
|
SingleMark thisMark1 = new SingleMark("", thisPoint1.getLat(), thisPoint1.getLng(), thisPoint1.getSeqID(), thisPoint1.getSeqID());
|
||||||
Limit thisPoint2 = courseLimits.get(0);
|
Limit thisPoint2 = courseLimits.get(0);
|
||||||
SingleMark thisMark2 = new SingleMark("", thisPoint2.getLat(), thisPoint2.getLng(), thisPoint2.getSeqID());
|
SingleMark thisMark2 = new SingleMark("", thisPoint2.getLat(), thisPoint2.getLng(), thisPoint2.getSeqID(), thisPoint2.getSeqID());
|
||||||
Point2D borderPoint1 = findScaledXY(thisMark1);
|
Point2D borderPoint1 = findScaledXY(thisMark1);
|
||||||
Point2D borderPoint2 = findScaledXY(thisMark2);
|
Point2D borderPoint2 = findScaledXY(thisMark2);
|
||||||
gc.strokeLine(borderPoint1.getX(), borderPoint1.getY(),
|
gc.strokeLine(borderPoint1.getX(), borderPoint1.getY(),
|
||||||
@@ -214,7 +214,7 @@ public class CanvasController {
|
|||||||
for (BoatGroup boatGroup : boatGroups) {
|
for (BoatGroup boatGroup : boatGroups) {
|
||||||
// some raceObjects will have multiple ID's (for instance gate marks)
|
// some raceObjects will have multiple ID's (for instance gate marks)
|
||||||
//checking if the current "ID" has any updates associated with it
|
//checking if the current "ID" has any updates associated with it
|
||||||
if (StreamParser.boatPositions.containsKey(boatGroup.getRaceId())) {
|
if (StreamParser.boatLocations.containsKey(boatGroup.getRaceId())) {
|
||||||
if (boatGroup.isStopped()) {
|
if (boatGroup.isStopped()) {
|
||||||
updateBoatGroup(boatGroup);
|
updateBoatGroup(boatGroup);
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,7 @@ public class CanvasController {
|
|||||||
}
|
}
|
||||||
for (MarkGroup markGroup : markGroups) {
|
for (MarkGroup markGroup : markGroups) {
|
||||||
for (Long id : markGroup.getRaceIds()) {
|
for (Long id : markGroup.getRaceIds()) {
|
||||||
if (StreamParser.markPositions.containsKey(id)) {
|
if (StreamParser.markLocations.containsKey(id)) {
|
||||||
updateMarkGroup(id, markGroup);
|
updateMarkGroup(id, markGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -240,7 +240,7 @@ public class CanvasController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateBoatGroup(BoatGroup boatGroup) {
|
private void updateBoatGroup(BoatGroup boatGroup) {
|
||||||
PriorityBlockingQueue<BoatPositionPacket> movementQueue = StreamParser.boatPositions.get(boatGroup.getRaceId());
|
PriorityBlockingQueue<BoatPositionPacket> movementQueue = StreamParser.boatLocations.get(boatGroup.getRaceId());
|
||||||
// giving the movementQueue a 5 packet buffer to account for slightly out of order packets
|
// giving the movementQueue a 5 packet buffer to account for slightly out of order packets
|
||||||
if (movementQueue.size() > 0){
|
if (movementQueue.size() > 0){
|
||||||
try {
|
try {
|
||||||
@@ -256,7 +256,7 @@ public class CanvasController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateMarkGroup (long raceId, MarkGroup markGroup) {
|
void updateMarkGroup (long raceId, MarkGroup markGroup) {
|
||||||
PriorityBlockingQueue<BoatPositionPacket> movementQueue = StreamParser.markPositions.get(raceId);
|
PriorityBlockingQueue<BoatPositionPacket> movementQueue = StreamParser.markLocations.get(raceId);
|
||||||
if (movementQueue.size() > 0){
|
if (movementQueue.size() > 0){
|
||||||
try {
|
try {
|
||||||
BoatPositionPacket positionPacket = movementQueue.take();
|
BoatPositionPacket positionPacket = movementQueue.take();
|
||||||
@@ -294,7 +294,7 @@ public class CanvasController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializeMarks() {
|
private void initializeMarks() {
|
||||||
ArrayList<Mark> allMarks = StreamParser.getXmlObject().getRaceXML().getCompoundMarks();
|
List<Mark> allMarks = StreamParser.getXmlObject().getRaceXML().getNonDupCompoundMarks();
|
||||||
for (Mark mark : allMarks) {
|
for (Mark mark : allMarks) {
|
||||||
if (mark.getMarkType() == MarkType.SINGLE_MARK) {
|
if (mark.getMarkType() == MarkType.SINGLE_MARK) {
|
||||||
SingleMark sMark = (SingleMark) mark;
|
SingleMark sMark = (SingleMark) mark;
|
||||||
@@ -382,15 +382,15 @@ public class CanvasController {
|
|||||||
sortedPoints.sort(Comparator.comparingDouble(Limit::getLat));
|
sortedPoints.sort(Comparator.comparingDouble(Limit::getLat));
|
||||||
Limit minLatMark = sortedPoints.get(0);
|
Limit minLatMark = sortedPoints.get(0);
|
||||||
Limit maxLatMark = sortedPoints.get(sortedPoints.size()-1);
|
Limit maxLatMark = sortedPoints.get(sortedPoints.size()-1);
|
||||||
minLatPoint = new SingleMark(minLatMark.toString(), minLatMark.getLat(), minLatMark.getLng(), minLatMark.getSeqID());
|
minLatPoint = new SingleMark(minLatMark.toString(), minLatMark.getLat(), minLatMark.getLng(), minLatMark.getSeqID(), minLatMark.getSeqID());
|
||||||
maxLatPoint = new SingleMark(maxLatMark.toString(), maxLatMark.getLat(), maxLatMark.getLng(), maxLatMark.getSeqID());
|
maxLatPoint = new SingleMark(maxLatMark.toString(), maxLatMark.getLat(), maxLatMark.getLng(), maxLatMark.getSeqID(), minLatMark.getSeqID());
|
||||||
|
|
||||||
sortedPoints.sort(Comparator.comparingDouble(Limit::getLng));
|
sortedPoints.sort(Comparator.comparingDouble(Limit::getLng));
|
||||||
//If the course is on a point on the earth where longitudes wrap around.
|
//If the course is on a point on the earth where longitudes wrap around.
|
||||||
Limit minLonMark = sortedPoints.get(0);
|
Limit minLonMark = sortedPoints.get(0);
|
||||||
Limit maxLonMark = sortedPoints.get(sortedPoints.size()-1);
|
Limit maxLonMark = sortedPoints.get(sortedPoints.size()-1);
|
||||||
minLonPoint = new SingleMark(minLonMark.toString(), minLonMark.getLat(), minLonMark.getLng(), minLonMark.getSeqID());
|
minLonPoint = new SingleMark(minLonMark.toString(), minLonMark.getLat(), minLonMark.getLng(), minLonMark.getSeqID(), minLonMark.getSeqID());
|
||||||
maxLonPoint = new SingleMark(maxLonMark.toString(), maxLonMark.getLat(), maxLonMark.getLng(), maxLonMark.getSeqID());
|
maxLonPoint = new SingleMark(maxLonMark.toString(), maxLonMark.getLat(), maxLonMark.getLng(), maxLonMark.getSeqID(), minLonMark.getSeqID());
|
||||||
if (maxLonPoint.getLongitude() - minLonPoint.getLongitude() > 180) {
|
if (maxLonPoint.getLongitude() - minLonPoint.getLongitude() > 180) {
|
||||||
horizontalInversion = true;
|
horizontalInversion = true;
|
||||||
}
|
}
|
||||||
@@ -462,7 +462,7 @@ public class CanvasController {
|
|||||||
return findScaledXY (unscaled.getLatitude(), unscaled.getLongitude());
|
return findScaledXY (unscaled.getLatitude(), unscaled.getLongitude());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Point2D findScaledXY (double unscaledLat, double unscaledLon) {
|
public Point2D findScaledXY (double unscaledLat, double unscaledLon) {
|
||||||
double distanceFromReference;
|
double distanceFromReference;
|
||||||
double angleFromReference;
|
double angleFromReference;
|
||||||
int xAxisLocation = (int) referencePointX;
|
int xAxisLocation = (int) referencePointX;
|
||||||
@@ -499,8 +499,8 @@ public class CanvasController {
|
|||||||
Point2D p1, p2;
|
Point2D p1, p2;
|
||||||
Mark m1, m2;
|
Mark m1, m2;
|
||||||
double theta, distance, dx, dy, dHorizontal, dVertical;
|
double theta, distance, dx, dy, dHorizontal, dVertical;
|
||||||
m1 = new SingleMark("m1", maxLatPoint.getLatitude(), minLonPoint.getLongitude(), 1);
|
m1 = new SingleMark("m1", maxLatPoint.getLatitude(), minLonPoint.getLongitude(), 1, 0);
|
||||||
m2 = new SingleMark("m2", minLatPoint.getLatitude(), maxLonPoint.getLongitude(), 2);
|
m2 = new SingleMark("m2", minLatPoint.getLatitude(), maxLonPoint.getLongitude(), 2, 0);
|
||||||
p1 = findScaledXY(m1);
|
p1 = findScaledXY(m1);
|
||||||
p2 = findScaledXY(m2);
|
p2 = findScaledXY(m2);
|
||||||
theta = Mark.calculateHeadingRad(m1, m2);
|
theta = Mark.calculateHeadingRad(m1, m2);
|
||||||
@@ -516,4 +516,8 @@ public class CanvasController {
|
|||||||
List<BoatGroup> getBoatGroups() {
|
List<BoatGroup> getBoatGroups() {
|
||||||
return boatGroups;
|
return boatGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<MarkGroup> getMarkGroups() {
|
||||||
|
return markGroups;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,6 @@ public class Controller implements Initializable {
|
|||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString());
|
||||||
setContentPane("/views/StartScreenView.fxml");
|
setContentPane("/views/StartScreenView.fxml");
|
||||||
StreamParser.boatPositions.clear();
|
StreamParser.boatLocations.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import javafx.collections.FXCollections;
|
|||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.geometry.Point2D;
|
||||||
import javafx.geometry.Side;
|
import javafx.geometry.Side;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.chart.LineChart;
|
import javafx.scene.chart.LineChart;
|
||||||
@@ -21,17 +22,24 @@ import javafx.scene.layout.Pane;
|
|||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.paint.Paint;
|
import javafx.scene.paint.Paint;
|
||||||
|
import javafx.scene.shape.Line;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.StageStyle;
|
import javafx.stage.StageStyle;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
|
import seng302.GeometryUtils;
|
||||||
import seng302.controllers.annotations.Annotation;
|
import seng302.controllers.annotations.Annotation;
|
||||||
import seng302.controllers.annotations.ImportantAnnotationController;
|
import seng302.controllers.annotations.ImportantAnnotationController;
|
||||||
import seng302.controllers.annotations.ImportantAnnotationDelegate;
|
import seng302.controllers.annotations.ImportantAnnotationDelegate;
|
||||||
import seng302.controllers.annotations.ImportantAnnotationsState;
|
import seng302.controllers.annotations.ImportantAnnotationsState;
|
||||||
import seng302.models.*;
|
import seng302.models.*;
|
||||||
|
import seng302.models.mark.GateMark;
|
||||||
|
import seng302.models.mark.Mark;
|
||||||
|
import seng302.models.mark.MarkGroup;
|
||||||
|
import seng302.models.mark.SingleMark;
|
||||||
import seng302.models.stream.StreamParser;
|
import seng302.models.stream.StreamParser;
|
||||||
|
import seng302.models.stream.XMLParser;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -39,7 +47,6 @@ import seng302.models.stream.XMLParser.RaceXMLObject.Participant;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Created by ptg19 on 29/03/17.
|
* Created by ptg19 on 29/03/17.
|
||||||
*/
|
*/
|
||||||
public class RaceViewController extends Thread implements ImportantAnnotationDelegate {
|
public class RaceViewController extends Thread implements ImportantAnnotationDelegate {
|
||||||
@@ -67,7 +74,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
@FXML
|
@FXML
|
||||||
private CanvasController includedCanvasController;
|
private CanvasController includedCanvasController;
|
||||||
|
|
||||||
private ArrayList<Yacht> startingBoats = new ArrayList<>();
|
private static ArrayList<Yacht> startingBoats = new ArrayList<>();
|
||||||
private boolean displayFps;
|
private boolean displayFps;
|
||||||
private Timeline timerTimeline;
|
private Timeline timerTimeline;
|
||||||
private Stage stage;
|
private Stage stage;
|
||||||
@@ -147,7 +154,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
(observable, oldValue, newValue) -> displayFps = !displayFps);
|
(observable, oldValue, newValue) -> displayFps = !displayFps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void initialiseAnnotationSlider() {
|
private void initialiseAnnotationSlider() {
|
||||||
annotationSlider.setLabelFormatter(new StringConverter<Double>() {
|
annotationSlider.setLabelFormatter(new StringConverter<Double>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -221,7 +227,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Adds the new data series to the sparkline (and set the colour of the series)
|
// Adds the new data series to the sparkline (and set the colour of the series)
|
||||||
raceSparkLine.setCreateSymbols(false);
|
raceSparkLine.setCreateSymbols(false);
|
||||||
positions.stream().filter(spark -> !raceSparkLine.getData().contains(spark)).forEach(spark -> {
|
positions.stream().filter(spark -> !raceSparkLine.getData().contains(spark)).forEach(spark -> {
|
||||||
@@ -254,6 +259,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
color = yacht.getColour();
|
color = yacht.getColour();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (color == null){
|
||||||
|
return String.format( "#%02X%02X%02X",255,255,255);
|
||||||
|
}
|
||||||
return String.format( "#%02X%02X%02X",
|
return String.format( "#%02X%02X%02X",
|
||||||
(int)( color.getRed() * 255 ),
|
(int)( color.getRed() * 255 ),
|
||||||
(int)( color.getGreen() * 255 ),
|
(int)( color.getGreen() * 255 ),
|
||||||
@@ -285,6 +293,40 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over all corners until ones SeqID matches with the boats current leg number.
|
||||||
|
* Then it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark
|
||||||
|
* Returns null if no next mark found.
|
||||||
|
* @param bg The BoatGroup to find the next mark of
|
||||||
|
* @return The next Mark or null if none found
|
||||||
|
*/
|
||||||
|
private Mark getNextMark(BoatGroup bg) {
|
||||||
|
Integer legNumber = bg.getBoat().getLegNumber();
|
||||||
|
|
||||||
|
List<XMLParser.RaceXMLObject.Corner> markSequence = StreamParser.getXmlObject().getRaceXML().getCompoundMarkSequence();
|
||||||
|
|
||||||
|
if (legNumber == 0) {
|
||||||
|
return null;
|
||||||
|
} else if (legNumber == markSequence.size() - 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (XMLParser.RaceXMLObject.Corner corner : markSequence) {
|
||||||
|
if (legNumber + 2 == corner.getSeqID()) {
|
||||||
|
Integer thisCompoundMarkID = corner.getCompoundMarkID();
|
||||||
|
|
||||||
|
for (Mark mark : StreamParser.getXmlObject().getRaceXML().getAllCompoundMarks()) {
|
||||||
|
if (mark.getCompoundMarkID() == thisCompoundMarkID) {
|
||||||
|
return mark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the wind direction arrow and text as from info from the StreamParser
|
* Updates the wind direction arrow and text as from info from the StreamParser
|
||||||
*/
|
*/
|
||||||
@@ -367,6 +409,94 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void updateLaylines(BoatGroup bg) {
|
||||||
|
|
||||||
|
Mark nextMark = getNextMark(bg);
|
||||||
|
Boolean isUpwind = null;
|
||||||
|
// Can only calc leg direction if there is a next mark and it is a gate mark
|
||||||
|
if (nextMark != null) {
|
||||||
|
if (nextMark instanceof GateMark) {
|
||||||
|
if (bg.isUpwindLeg(includedCanvasController, nextMark)) {
|
||||||
|
isUpwind = true;
|
||||||
|
} else {
|
||||||
|
isUpwind = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(MarkGroup mg : includedCanvasController.getMarkGroups()) {
|
||||||
|
|
||||||
|
mg.removeLaylines();
|
||||||
|
|
||||||
|
if (mg.getMainMark().getId() == nextMark.getId()) {
|
||||||
|
|
||||||
|
SingleMark singleMark1 = ((GateMark) nextMark).getSingleMark1();
|
||||||
|
SingleMark singleMark2 = ((GateMark) nextMark).getSingleMark2();
|
||||||
|
Point2D markPoint1 = includedCanvasController.findScaledXY(singleMark1.getLatitude(), singleMark1.getLongitude());
|
||||||
|
Point2D markPoint2 = includedCanvasController.findScaledXY(singleMark2.getLatitude(), singleMark2.getLongitude());
|
||||||
|
HashMap<Double, Double> angleAndSpeed;
|
||||||
|
if (isUpwind) {
|
||||||
|
angleAndSpeed = PolarTable.getOptimalUpwindVMG(StreamParser.getWindSpeed());
|
||||||
|
} else {
|
||||||
|
angleAndSpeed = PolarTable.getOptimalDownwindVMG(StreamParser.getWindSpeed());
|
||||||
|
}
|
||||||
|
|
||||||
|
Double resultingAngle = angleAndSpeed.keySet().iterator().next();
|
||||||
|
|
||||||
|
|
||||||
|
Point2D boatCurrentPos = new Point2D(bg.getBoatLayoutX(), bg.getBoatLayoutY());
|
||||||
|
Point2D gateMidPoint = markPoint1.midpoint(markPoint2);
|
||||||
|
Integer lineFuncResult = GeometryUtils.lineFunction(boatCurrentPos, gateMidPoint, markPoint2);
|
||||||
|
Line rightLayline = new Line();
|
||||||
|
Line leftLayline = new Line();
|
||||||
|
if (lineFuncResult == 1) {
|
||||||
|
rightLayline = makeRightLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection());
|
||||||
|
leftLayline = makeLeftLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection());
|
||||||
|
} else if (lineFuncResult == -1) {
|
||||||
|
rightLayline = makeRightLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection());
|
||||||
|
leftLayline = makeLeftLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection());
|
||||||
|
}
|
||||||
|
|
||||||
|
leftLayline.setStrokeWidth(0.5);
|
||||||
|
leftLayline.setStroke(bg.getBoat().getColour());
|
||||||
|
|
||||||
|
rightLayline.setStrokeWidth(0.5);
|
||||||
|
rightLayline.setStroke(bg.getBoat().getColour());
|
||||||
|
|
||||||
|
bg.setLaylines(leftLayline, rightLayline);
|
||||||
|
mg.addLaylines(leftLayline, rightLayline);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Point2D getPointRotation(Point2D ref, Double distance, Double angle){
|
||||||
|
Double newX = ref.getX() + (ref.getX() + distance -ref.getX())*Math.cos(angle) - (ref.getY() + distance -ref.getY())*Math.sin(angle);
|
||||||
|
Double newY = ref.getY() + (ref.getX() + distance -ref.getX())*Math.sin(angle) + (ref.getY() + distance -ref.getY())*Math.cos(angle);
|
||||||
|
|
||||||
|
return new Point2D(newX, newY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Line makeLeftLayline(Point2D startPoint, Double layLineAngle, Double baseAngle) {
|
||||||
|
|
||||||
|
Point2D ep = getPointRotation(startPoint, 50.0, baseAngle + layLineAngle);
|
||||||
|
Line line = new Line(startPoint.getX(), startPoint.getY(), ep.getX(), ep.getY());
|
||||||
|
return line;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Line makeRightLayline(Point2D startPoint, Double layLineAngle, Double baseAngle) {
|
||||||
|
|
||||||
|
Point2D ep = getPointRotation(startPoint, 50.0, baseAngle - layLineAngle);
|
||||||
|
Line line = new Line(startPoint.getX(), startPoint.getY(), ep.getX(), ep.getY());
|
||||||
|
return line;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialised the combo box with any boats currently in the race and adds the required listener
|
* Initialised the combo box with any boats currently in the race and adds the required listener
|
||||||
* for the combobox to take action upon selection
|
* for the combobox to take action upon selection
|
||||||
@@ -443,7 +573,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the important annotations for a specific BoatGroup
|
* Display the important annotations for a specific BoatGroup
|
||||||
*
|
|
||||||
* @param bg The boat group to set the annotations for
|
* @param bg The boat group to set the annotations for
|
||||||
*/
|
*/
|
||||||
private void setBoatGroupImportantAnnotations(BoatGroup bg) {
|
private void setBoatGroupImportantAnnotations(BoatGroup bg) {
|
||||||
@@ -528,6 +657,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
|
|||||||
//We need to iterate over all race groups to get the matching boat group belonging to this boat if we
|
//We need to iterate over all race groups to get the matching boat group belonging to this boat if we
|
||||||
//are to toggle its annotations, there is no other backwards knowledge of a yacht to its boatgroup.
|
//are to toggle its annotations, there is no other backwards knowledge of a yacht to its boatgroup.
|
||||||
if (bg.getBoat().getHullID().equals(yacht.getHullID())) {
|
if (bg.getBoat().getHullID().equals(yacht.getHullID())) {
|
||||||
|
updateLaylines(bg);
|
||||||
bg.setIsSelected(true);
|
bg.setIsSelected(true);
|
||||||
selectedBoat = yacht;
|
selectedBoat = yacht;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ public class StartScreenController implements Initializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void switchToRaceView() {
|
public void switchToRaceView() {
|
||||||
StreamParser.boatPositions.clear();
|
StreamParser.boatLocations.clear();
|
||||||
switchedToRaceView = true;
|
switchedToRaceView = true;
|
||||||
setContentPane("/views/RaceView.fxml");
|
setContentPane("/views/RaceView.fxml");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package seng302.models;
|
package seng302.models;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
import javafx.scene.CacheHint;
|
import javafx.scene.CacheHint;
|
||||||
import javafx.scene.Group;
|
import javafx.scene.Group;
|
||||||
@@ -8,6 +10,11 @@ import javafx.scene.shape.Line;
|
|||||||
import javafx.scene.shape.Polygon;
|
import javafx.scene.shape.Polygon;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import javafx.scene.transform.Rotate;
|
import javafx.scene.transform.Rotate;
|
||||||
|
import seng302.GeometryUtils;
|
||||||
|
import seng302.controllers.CanvasController;
|
||||||
|
import seng302.models.mark.GateMark;
|
||||||
|
import seng302.models.mark.Mark;
|
||||||
|
import seng302.models.mark.SingleMark;
|
||||||
import seng302.models.stream.StreamParser;
|
import seng302.models.stream.StreamParser;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
@@ -50,6 +57,8 @@ public class BoatGroup extends Group {
|
|||||||
private Text estTimeToNextMarkObject;
|
private Text estTimeToNextMarkObject;
|
||||||
private Text legTimeObject;
|
private Text legTimeObject;
|
||||||
private Wake wake;
|
private Wake wake;
|
||||||
|
private Line leftLayLine;
|
||||||
|
private Line rightLayline;
|
||||||
private Double distanceTravelled = 0.0;
|
private Double distanceTravelled = 0.0;
|
||||||
private Point2D lastPoint;
|
private Point2D lastPoint;
|
||||||
private boolean destinationSet;
|
private boolean destinationSet;
|
||||||
@@ -149,10 +158,13 @@ public class BoatGroup extends Group {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
leftLayLine = new Line();
|
||||||
|
rightLayline = new Line();
|
||||||
|
|
||||||
wake = new Wake(0, -BOAT_HEIGHT);
|
wake = new Wake(0, -BOAT_HEIGHT);
|
||||||
super.getChildren()
|
super.getChildren()
|
||||||
.addAll(teamNameObject, velocityObject, boatPoly, estTimeToNextMarkObject,
|
.addAll(teamNameObject, velocityObject, boatPoly, estTimeToNextMarkObject,
|
||||||
legTimeObject);
|
legTimeObject, leftLayLine, rightLayline);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -359,6 +371,44 @@ public class BoatGroup extends Group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function works out if a boat is going upwind or down wind. It looks at the boats current position, the next
|
||||||
|
* gates position and the current wind
|
||||||
|
* If bot the wind vector from the next gate and the boat from the next gate lay on the same side, then the boat is
|
||||||
|
* going up wind, if they are on different sides of the gate, then the boat is going downwind
|
||||||
|
* @param canvasController
|
||||||
|
*/
|
||||||
|
public Boolean isUpwindLeg(CanvasController canvasController, Mark nextMark) {
|
||||||
|
|
||||||
|
Double windAngle = StreamParser.getWindDirection();
|
||||||
|
GateMark thisGateMark = (GateMark) nextMark;
|
||||||
|
SingleMark nextMark1 = thisGateMark.getSingleMark1();
|
||||||
|
SingleMark nextMark2 = thisGateMark.getSingleMark2();
|
||||||
|
Point2D nextMarkPoint1 = canvasController.findScaledXY(nextMark1.getLatitude(), nextMark1.getLongitude());
|
||||||
|
Point2D nextMarkPoint2 = canvasController.findScaledXY(nextMark2.getLatitude(), nextMark2.getLongitude());
|
||||||
|
|
||||||
|
Point2D boatCurrentPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY());
|
||||||
|
Point2D windTestPoint = GeometryUtils.makeArbitraryVectorPoint(nextMarkPoint1, windAngle, 10d);
|
||||||
|
|
||||||
|
|
||||||
|
Integer boatLineFuncResult = GeometryUtils.lineFunction(nextMarkPoint1, nextMarkPoint2, boatCurrentPoint);
|
||||||
|
Integer windLineFuncResult = GeometryUtils.lineFunction(nextMarkPoint1, nextMarkPoint2, windTestPoint);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
If both the wind vector from the gate and the boat from the gate are on the same side of that gate, then the
|
||||||
|
boat is travelling into the wind. thus upwind. Otherwise if they are on different sides, then the boat is going
|
||||||
|
with the wind.
|
||||||
|
*/
|
||||||
|
if (boatLineFuncResult == windLineFuncResult) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setIsSelected(Boolean isSelected) {
|
public void setIsSelected(Boolean isSelected) {
|
||||||
this.isSelected = isSelected;
|
this.isSelected = isSelected;
|
||||||
setTeamNameObjectVisible(isSelected);
|
setTeamNameObjectVisible(isSelected);
|
||||||
@@ -367,6 +417,7 @@ public class BoatGroup extends Group {
|
|||||||
setWakeVisible(isSelected);
|
setWakeVisible(isSelected);
|
||||||
setEstTimeToNextMarkObjectVisible(isSelected);
|
setEstTimeToNextMarkObjectVisible(isSelected);
|
||||||
setLegTimeObjectVisible(isSelected);
|
setLegTimeObjectVisible(isSelected);
|
||||||
|
setLayLinesVisible(isSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -394,6 +445,23 @@ public class BoatGroup extends Group {
|
|||||||
wake.setVisible(visible);
|
wake.setVisible(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLayLinesVisible(Boolean visible) {
|
||||||
|
leftLayLine.setVisible(visible);
|
||||||
|
rightLayline.setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLaylines(Line line1, Line line2) {
|
||||||
|
this.leftLayLine = line1;
|
||||||
|
this.rightLayline = line2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<Line> getLaylines() {
|
||||||
|
ArrayList<Line> laylines = new ArrayList<>();
|
||||||
|
laylines.add(leftLayLine);
|
||||||
|
laylines.add(rightLayline);
|
||||||
|
return laylines;
|
||||||
|
}
|
||||||
|
|
||||||
public Yacht getBoat() {
|
public Yacht getBoat() {
|
||||||
return boat;
|
return boat;
|
||||||
}
|
}
|
||||||
@@ -420,6 +488,16 @@ public class BoatGroup extends Group {
|
|||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Double getBoatLayoutX() {
|
||||||
|
return boatPoly.getLayoutX();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Double getBoatLayoutY() {
|
||||||
|
return boatPoly.getLayoutY();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isStopped() {
|
public boolean isStopped() {
|
||||||
return isStopped;
|
return isStopped;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,171 +0,0 @@
|
|||||||
package seng302.models;
|
|
||||||
|
|
||||||
import seng302.models.mark.Mark;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event class containing the time of specific event, related team/boat, and
|
|
||||||
* event location such as leg.
|
|
||||||
*/
|
|
||||||
public class Event {
|
|
||||||
private Double time; // Time the event occurs
|
|
||||||
private Yacht boat;
|
|
||||||
private boolean isFinishingEvent = false; // This event occurs when a boat finishes the race
|
|
||||||
private Mark mark1; // This mark
|
|
||||||
private Mark mark2; // Next mark
|
|
||||||
private int markPosInRace; // the position of the current mark in the race course
|
|
||||||
private double heading;
|
|
||||||
private final double ORIGIN_LAT = 32.320504;
|
|
||||||
private final double ORIGIN_LON = -64.857063;
|
|
||||||
private final double SCALE = 16000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event class containing the time of specific event, related team/boat, and
|
|
||||||
* event location such as leg.
|
|
||||||
*
|
|
||||||
* @param eventTime, what time the event happens
|
|
||||||
* @param eventBoat, the boat that the event belongs to
|
|
||||||
*/
|
|
||||||
public Event(Double eventTime, Yacht eventBoat, Mark mark1, Mark mark2, int markPosInRace) {
|
|
||||||
this.time = eventTime;
|
|
||||||
this.boat = eventBoat;
|
|
||||||
this.mark1 = mark1;
|
|
||||||
this.mark2 = mark2;
|
|
||||||
this.markPosInRace = markPosInRace;
|
|
||||||
this.heading = angleFromCoordinate(mark1, mark2);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event class containing the time of specific event, related team/boat, and
|
|
||||||
* event location such as leg.
|
|
||||||
*
|
|
||||||
* @param eventTime, what time the event happens
|
|
||||||
* @param eventBoat, the boat that the event belongs to
|
|
||||||
*/
|
|
||||||
public Event(Double eventTime, Yacht eventBoat, Mark mark1, int markPosInRace) {
|
|
||||||
this.time = eventTime;
|
|
||||||
this.boat = eventBoat;
|
|
||||||
this.mark1 = mark1;
|
|
||||||
this.markPosInRace = markPosInRace;
|
|
||||||
this.isFinishingEvent = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getTime() {
|
|
||||||
return this.time;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTime(double eventTime) {
|
|
||||||
this.time = eventTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the time in a formatted string
|
|
||||||
*
|
|
||||||
* @return the string of time
|
|
||||||
*/
|
|
||||||
public String getTimeString() {
|
|
||||||
return (new SimpleDateFormat("mm:ss:SSS")).format(new Date(time.longValue()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Yacht getBoat() {
|
|
||||||
return this.boat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBoat(Yacht eventBoat) {
|
|
||||||
this.boat = eventBoat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getIsFinishingEvent() {
|
|
||||||
return this.isFinishingEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a string that contains the timestamp and course information for this event
|
|
||||||
*
|
|
||||||
* @return A string that details what happened in this event
|
|
||||||
*/
|
|
||||||
public String getEventString() {
|
|
||||||
// This event is a boat finishing the race
|
|
||||||
if (this.isFinishingEvent) {
|
|
||||||
return (this.getTimeString() + ", " + this.getBoat().getBoatName() + " finished the race");
|
|
||||||
}
|
|
||||||
return (this.getTimeString() + ", " + this.getBoat().getBoatName() + " passed " + this.mark1.getName() + " going heading " + this.getBoatHeading() + "°");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the distance between the two marks
|
|
||||||
*/
|
|
||||||
public double getDistanceBetweenMarks() {
|
|
||||||
double earth_radius = 6378.137;
|
|
||||||
double dLat = this.mark2.getLatitude() * Math.PI / 180 - this.mark1.getLatitude() * Math.PI / 180;
|
|
||||||
double dLon = this.mark2.getLongitude() * Math.PI / 180 - this.mark1.getLongitude() * Math.PI / 180;
|
|
||||||
|
|
||||||
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(this.mark1.getLatitude() * Math.PI / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
|
||||||
|
|
||||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
||||||
double d = earth_radius * c;
|
|
||||||
|
|
||||||
return d * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates current boat heading direction.
|
|
||||||
* @return the boats heading as degree. vertical upward is 0 degree, and degree goes up clockwise.
|
|
||||||
*/
|
|
||||||
public double getBoatHeading() {
|
|
||||||
if (mark2 == null){
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double x1 = (mark1.getLongitude() - ORIGIN_LON) * SCALE;
|
|
||||||
double y1 = (ORIGIN_LAT - mark1.getLatitude()) * SCALE;
|
|
||||||
double x2 = (mark2.getLongitude() - ORIGIN_LON) * SCALE;
|
|
||||||
double y2 = (ORIGIN_LAT - mark2.getLatitude()) * SCALE;
|
|
||||||
|
|
||||||
double headingRadians = Math.atan2(y2-y1, x2-x1);
|
|
||||||
|
|
||||||
if (headingRadians < 0){
|
|
||||||
headingRadians += 2 * Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert back to degrees, and flip 180 degrees
|
|
||||||
// return ((headingRadians) * 180) / Math.PI;
|
|
||||||
return (Math.toDegrees(headingRadians) + 90) % 360;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the angle between to angular co-ordinates on a sphere.
|
|
||||||
*
|
|
||||||
* @param geoPointOne first geographical location
|
|
||||||
* @param geoPointTwo second geographical location
|
|
||||||
* @return the angle from point one to point two
|
|
||||||
*/
|
|
||||||
private Double angleFromCoordinate(Mark geoPointOne, Mark geoPointTwo) {
|
|
||||||
if (geoPointTwo == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
double x1 = geoPointOne.getLatitude();
|
|
||||||
double y1 = -geoPointOne.getLongitude();
|
|
||||||
double x2 = geoPointTwo.getLatitude();
|
|
||||||
double y2 = -geoPointTwo.getLongitude();
|
|
||||||
|
|
||||||
return Math.toDegrees(Math.atan2(x2-x1, y2-y1));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getHeading() {
|
|
||||||
return heading;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mark getThisMark() {
|
|
||||||
return this.mark1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMarkPosInRace() {
|
|
||||||
return markPosInRace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
package seng302.models;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A static class for parsing and storing the polars. Will parse the whole polar table and also store the optimised
|
||||||
|
* upwind and downwind in separate tables here as well
|
||||||
|
* Created by wmu16 on 22/05/17.
|
||||||
|
*/
|
||||||
|
public final class PolarTable {
|
||||||
|
|
||||||
|
//A Polar table will consist of a wind speed key to a hashmap value of pairs of wind angles and boat speeds
|
||||||
|
private static HashMap<Double, HashMap<Double, Double>> polarTable;
|
||||||
|
private static HashMap<Double, HashMap<Double, Double>> upwindOptimal;
|
||||||
|
private static HashMap<Double, HashMap<Double, Double>> downwindOptimal;
|
||||||
|
|
||||||
|
private static int upTwaIndex;
|
||||||
|
private static int dnTwaIndex;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through each row of the polar table, in pairs, to extract the row into a hashmap of angle to boat speed.
|
||||||
|
* These angle boatspeed hashmaps are then added to an outer hashmap at the end of wind speed key to each row hashmap
|
||||||
|
* as a value
|
||||||
|
* @param file containing the polar csv information
|
||||||
|
*/
|
||||||
|
public static void parsePolarFile(String file) {
|
||||||
|
polarTable = new HashMap<>();
|
||||||
|
upwindOptimal = new HashMap<>();
|
||||||
|
downwindOptimal = new HashMap<>();
|
||||||
|
|
||||||
|
String line;
|
||||||
|
Boolean isHeaderLine = true;
|
||||||
|
|
||||||
|
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
String[] thisLine = line.split(",");
|
||||||
|
|
||||||
|
//Initial line in file
|
||||||
|
if (isHeaderLine) {
|
||||||
|
deduceHeaders(thisLine);
|
||||||
|
isHeaderLine = false;
|
||||||
|
} else {
|
||||||
|
HashMap<Double, Double> thisPolar = new HashMap<>();
|
||||||
|
HashMap<Double, Double> thisUpWindPolar = new HashMap<>();
|
||||||
|
HashMap<Double, Double> thisDnWindPolar = new HashMap<>();
|
||||||
|
Double thisWindSpeed = Double.parseDouble(thisLine[0]);
|
||||||
|
|
||||||
|
// -3 <== -1 for length -1, and a further -2 as we iterate in pairs of 2 so finish before final 2
|
||||||
|
for (int i = 1; i < thisLine.length; i += 2) {
|
||||||
|
Double thisWindAngle = Double.parseDouble(thisLine[i]);
|
||||||
|
Double thisBoatSpeed = Double.parseDouble(thisLine[i + 1]);
|
||||||
|
thisPolar.put(thisWindAngle, thisBoatSpeed);
|
||||||
|
if (i == upTwaIndex) {
|
||||||
|
thisUpWindPolar.put(thisWindAngle, thisBoatSpeed);
|
||||||
|
} else if (i == dnTwaIndex) {
|
||||||
|
thisDnWindPolar.put(thisWindAngle, thisBoatSpeed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
polarTable.put(thisWindSpeed, thisPolar);
|
||||||
|
upwindOptimal.put(thisWindSpeed, thisUpWindPolar);
|
||||||
|
downwindOptimal.put(thisWindSpeed, thisDnWindPolar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the header line of a polar file
|
||||||
|
* @param thisLine The line which is the header of a polar file
|
||||||
|
*/
|
||||||
|
private static void deduceHeaders(String[] thisLine) {
|
||||||
|
|
||||||
|
for (int i = 0; i < thisLine.length; i++) {
|
||||||
|
String thisItem = thisLine[i];
|
||||||
|
if (thisItem.toLowerCase().startsWith("uptwa")) {
|
||||||
|
upTwaIndex = i;
|
||||||
|
}
|
||||||
|
else if (thisItem.toLowerCase().startsWith("dntwa")) {
|
||||||
|
dnTwaIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The entire polar table
|
||||||
|
*/
|
||||||
|
public static HashMap<Double, HashMap<Double, Double>> getPolarTable() {
|
||||||
|
return polarTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The polar table just containing the optimal upwind values
|
||||||
|
*/
|
||||||
|
public static HashMap<Double, HashMap<Double, Double>> getUpwindOptimal() {
|
||||||
|
return upwindOptimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The polar table just containing the optimal downwind values
|
||||||
|
*/
|
||||||
|
public static HashMap<Double, HashMap<Double, Double>> getDownwindOptimal() {
|
||||||
|
return downwindOptimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will raise an exception if a polar table has just one row of data
|
||||||
|
* @param thisWindSpeed The current wind speed
|
||||||
|
* @return HashMap containing just the optimal upwind angle and resulting boat speed
|
||||||
|
*/
|
||||||
|
public static HashMap<Double, Double> getOptimalUpwindVMG(Double thisWindSpeed) {
|
||||||
|
|
||||||
|
Double polarWindSpeed = getClosestMatch(thisWindSpeed);
|
||||||
|
return upwindOptimal.get(polarWindSpeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will raise an exception if a polar table has just one row of data
|
||||||
|
* @param thisWindSpeed The current wind speed
|
||||||
|
* @return HashMap containing just the optimal downwind angle and resulting boat speed
|
||||||
|
*/
|
||||||
|
public static HashMap<Double, Double> getOptimalDownwindVMG(Double thisWindSpeed) {
|
||||||
|
|
||||||
|
Double polarWindSpeed = getClosestMatch(thisWindSpeed);
|
||||||
|
return downwindOptimal.get(polarWindSpeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Double getClosestMatch(Double thisWindSpeed) {
|
||||||
|
|
||||||
|
ArrayList<Double> windValues = new ArrayList<>(polarTable.keySet());
|
||||||
|
|
||||||
|
Double lowerVal = windValues.get(0);
|
||||||
|
Double upperVal = windValues.get(1);
|
||||||
|
|
||||||
|
for(int i = 0; i < windValues.size() - 1; i++) {
|
||||||
|
lowerVal = windValues.get(i);
|
||||||
|
upperVal = windValues.get(i+1);
|
||||||
|
if (thisWindSpeed <= upperVal) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Double lowerDiff = Math.abs(lowerVal - thisWindSpeed);
|
||||||
|
Double upperDiff = Math.abs(upperVal - thisWindSpeed);
|
||||||
|
|
||||||
|
return (lowerDiff <= upperDiff) ? lowerVal : upperVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
package seng302.models;
|
package seng302.models;
|
||||||
|
|
||||||
|
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
|
import seng302.models.mark.Mark;
|
||||||
import seng302.controllers.RaceViewController;
|
import seng302.controllers.RaceViewController;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import seng302.models.stream.StreamParser;
|
||||||
|
import seng302.models.stream.XMLParser.RaceXMLObject.Corner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Yacht class for the racing boat.
|
* Yacht class for the racing boat.
|
||||||
@@ -35,6 +37,8 @@ public class Yacht {
|
|||||||
private String position;
|
private String position;
|
||||||
// Mark rounding
|
// Mark rounding
|
||||||
private Long markRoundingTime;
|
private Long markRoundingTime;
|
||||||
|
private Mark lastMarkRounded;
|
||||||
|
private Mark nextMark;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -181,8 +185,24 @@ public class Yacht {
|
|||||||
this.markRoundingTime = markRoundingTime;
|
this.markRoundingTime = markRoundingTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Mark getLastMarkRounded() {
|
||||||
|
return lastMarkRounded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastMarkRounded(Mark lastMarkRounded) {
|
||||||
|
this.lastMarkRounded = lastMarkRounded;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return boatName;
|
return boatName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setNextMark(Mark nextMark) {
|
||||||
|
this.nextMark = nextMark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mark getNextMark(){
|
||||||
|
return nextMark;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ public class GateMark extends Mark {
|
|||||||
* @param singleMark1 one single mark inside of the gate mark
|
* @param singleMark1 one single mark inside of the gate mark
|
||||||
* @param singleMark2 the second mark inside of the gate mark
|
* @param singleMark2 the second mark inside of the gate mark
|
||||||
*/
|
*/
|
||||||
public GateMark(String name, MarkType type, SingleMark singleMark1, SingleMark singleMark2, double latitude, double longitude) {
|
public GateMark(String name, MarkType type, SingleMark singleMark1, SingleMark singleMark2, double latitude, double longitude, int compoundMarkID) {
|
||||||
super(name, type, latitude, longitude);
|
super(name, type, latitude, longitude, compoundMarkID);
|
||||||
this.singleMark1 = singleMark1;
|
this.singleMark1 = singleMark1;
|
||||||
this.singleMark2 = singleMark2;
|
this.singleMark2 = singleMark2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,25 +11,27 @@ public abstract class Mark {
|
|||||||
private double latitude;
|
private double latitude;
|
||||||
private double longitude;
|
private double longitude;
|
||||||
private long id;
|
private long id;
|
||||||
|
private int compoundMarkID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a mark instance by passing its name and type
|
* Create a mark instance by passing its name and type
|
||||||
*
|
|
||||||
* @param name the name of the mark
|
* @param name the name of the mark
|
||||||
* @param markType the type of mark. either GATE_MARK or SINGLE_MARK.
|
* @param markType the type of mark. either GATE_MARK or SINGLE_MARK.
|
||||||
*/
|
*/
|
||||||
public Mark(String name, MarkType markType, int id) {
|
public Mark (String name, MarkType markType, int sourceID, int compoundMarkID) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.markType = markType;
|
this.markType = markType;
|
||||||
this.id = id;
|
this.id = sourceID;
|
||||||
|
this.compoundMarkID = compoundMarkID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mark(String name, MarkType markType, double latitude, double longitude) {
|
public Mark(String name, MarkType markType, double latitude, double longitude, int compoundMarkID) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.markType = markType;
|
this.markType = markType;
|
||||||
this.latitude = latitude;
|
this.latitude = latitude;
|
||||||
this.longitude = longitude;
|
this.longitude = longitude;
|
||||||
id = 0;
|
this.id = 0;
|
||||||
|
this.compoundMarkID = compoundMarkID;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -139,4 +141,8 @@ public abstract class Mark {
|
|||||||
public void setId(int id) {
|
public void setId(int id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getCompoundMarkID() {
|
||||||
|
return compoundMarkID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
import javafx.scene.Group;
|
import javafx.scene.Group;
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.shape.Circle;
|
import javafx.scene.shape.Circle;
|
||||||
import javafx.scene.shape.Line;
|
import javafx.scene.shape.Line;
|
||||||
|
import seng302.GeometryUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Grouping of javaFX objects needed to represent a Mark on screen.
|
* Grouping of javaFX objects needed to represent a Mark on screen.
|
||||||
@@ -45,6 +47,29 @@ public class MarkGroup extends Group {
|
|||||||
super.getChildren().add(markCircle);
|
super.getChildren().add(markCircle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addLaylines(Line line1, Line line2) {
|
||||||
|
|
||||||
|
super.getChildren().addAll(line1, line2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void removeLaylines() {
|
||||||
|
ArrayList<Node> toRemove = new ArrayList<>();
|
||||||
|
for(Node node : super.getChildren()) {
|
||||||
|
if (node instanceof Line) {
|
||||||
|
Line layLine = (Line) node;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* OOHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHhhh
|
||||||
|
*/
|
||||||
|
if (layLine.getStrokeWidth() == 0.5){
|
||||||
|
toRemove.add(layLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.getChildren().removeAll(toRemove);
|
||||||
|
}
|
||||||
|
|
||||||
public MarkGroup(GateMark mark, Point2D points1, Point2D points2) {
|
public MarkGroup(GateMark mark, Point2D points1, Point2D points2) {
|
||||||
marks.add(mark.getSingleMark1());
|
marks.add(mark.getSingleMark1());
|
||||||
marks.add(mark.getSingleMark2());
|
marks.add(mark.getSingleMark2());
|
||||||
@@ -84,6 +109,11 @@ public class MarkGroup extends Group {
|
|||||||
}
|
}
|
||||||
super.getChildren().add(line);
|
super.getChildren().add(line);
|
||||||
|
|
||||||
|
//Laylines
|
||||||
|
// if (mark.)
|
||||||
|
|
||||||
|
// addLayLine(points1, 12.0, 90.0);
|
||||||
|
// addLayLine(points2, 12.0, 90.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void moveMarkTo (double x, double y, long raceId)
|
public void moveMarkTo (double x, double y, long raceId)
|
||||||
@@ -126,4 +156,8 @@ public class MarkGroup extends Group {
|
|||||||
idArray[i++] = mark.getId();
|
idArray[i++] = mark.getId();
|
||||||
return idArray;
|
return idArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Mark getMainMark() {
|
||||||
|
return mainMark;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,6 @@ public class SingleMark extends Mark {
|
|||||||
private double lat;
|
private double lat;
|
||||||
private double lon;
|
private double lon;
|
||||||
private String name;
|
private String name;
|
||||||
private int id;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a marker
|
* Represents a marker
|
||||||
@@ -18,24 +17,12 @@ public class SingleMark extends Mark {
|
|||||||
* @param lat, the latitude of the marker
|
* @param lat, the latitude of the marker
|
||||||
* @param lon, the longitude of the marker
|
* @param lon, the longitude of the marker
|
||||||
*/
|
*/
|
||||||
public SingleMark(String name, double lat, double lon, int id) {
|
public SingleMark(String name, double lat, double lon, int sourceID, int compoundMarkID) {
|
||||||
super(name, MarkType.SINGLE_MARK, id);
|
super(name, MarkType.SINGLE_MARK, sourceID, compoundMarkID);
|
||||||
this.lat = lat;
|
this.lat = lat;
|
||||||
this.lon = lon;
|
this.lon = lon;
|
||||||
this.id = id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the marker at the beginning of a leg
|
|
||||||
*
|
|
||||||
* @param name, the name of the marker
|
|
||||||
*/
|
|
||||||
public SingleMark(String name) {
|
|
||||||
super(name, MarkType.SINGLE_MARK, 0);
|
|
||||||
this.lat = 0;
|
|
||||||
this.lon = 0;
|
|
||||||
this.id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getLatitude() {
|
public double getLatitude() {
|
||||||
return this.lat;
|
return this.lat;
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
@@ -23,6 +22,7 @@ import org.w3c.dom.Document;
|
|||||||
import org.xml.sax.InputSource;
|
import org.xml.sax.InputSource;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
import seng302.models.Yacht;
|
import seng302.models.Yacht;
|
||||||
|
import seng302.models.mark.Mark;
|
||||||
import seng302.models.stream.packets.BoatPositionPacket;
|
import seng302.models.stream.packets.BoatPositionPacket;
|
||||||
import seng302.models.stream.packets.StreamPacket;
|
import seng302.models.stream.packets.StreamPacket;
|
||||||
|
|
||||||
@@ -34,8 +34,8 @@ import seng302.models.stream.packets.StreamPacket;
|
|||||||
*/
|
*/
|
||||||
public class StreamParser extends Thread {
|
public class StreamParser extends Thread {
|
||||||
|
|
||||||
public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> markPositions = new ConcurrentHashMap<>();
|
public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> markLocations = new ConcurrentHashMap<>();
|
||||||
public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> boatPositions = new ConcurrentHashMap<>();
|
public static ConcurrentHashMap<Long, PriorityBlockingQueue<BoatPositionPacket>> boatLocations = new ConcurrentHashMap<>();
|
||||||
private String threadName;
|
private String threadName;
|
||||||
private Thread t;
|
private Thread t;
|
||||||
private static boolean newRaceXmlReceived = false;
|
private static boolean newRaceXmlReceived = false;
|
||||||
@@ -45,12 +45,17 @@ public class StreamParser extends Thread {
|
|||||||
private static boolean streamStatus = false;
|
private static boolean streamStatus = false;
|
||||||
private static long timeSinceStart = -1;
|
private static long timeSinceStart = -1;
|
||||||
private static Map<Integer, Yacht> boats = new ConcurrentHashMap<>();
|
private static Map<Integer, Yacht> boats = new ConcurrentHashMap<>();
|
||||||
private static Map<Long, Yacht> boatsPos = new ConcurrentSkipListMap<>();
|
private static Map<Integer, Yacht> boatsPos = new ConcurrentSkipListMap<>();
|
||||||
private static double windDirection = 0;
|
private static double windDirection = 0;
|
||||||
|
private static Double windSpeed = 0d;
|
||||||
private static Long currentTimeLong;
|
private static Long currentTimeLong;
|
||||||
private static String currentTimeString;
|
private static String currentTimeString;
|
||||||
private static boolean appRunning;
|
private static boolean appRunning;
|
||||||
|
|
||||||
|
|
||||||
|
//CONVERSION CONSTANTS
|
||||||
|
private static final Double MS_TO_KNOTS = 1.94384;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to initialise the thread name and stream parser object so a thread can be executed
|
* Used to initialise the thread name and stream parser object so a thread can be executed
|
||||||
*
|
*
|
||||||
@@ -145,6 +150,7 @@ public class StreamParser extends Thread {
|
|||||||
}
|
}
|
||||||
} catch (NullPointerException e) {
|
} catch (NullPointerException e) {
|
||||||
System.out.println("Error parsing packet");
|
System.out.println("Error parsing packet");
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +195,7 @@ public class StreamParser extends Thread {
|
|||||||
int raceStatus = payload[11];
|
int raceStatus = payload[11];
|
||||||
long expectedStartTime = bytesToLong(Arrays.copyOfRange(payload,12,18));
|
long expectedStartTime = bytesToLong(Arrays.copyOfRange(payload,12,18));
|
||||||
long windDir = bytesToLong(Arrays.copyOfRange(payload,18,20));
|
long windDir = bytesToLong(Arrays.copyOfRange(payload,18,20));
|
||||||
long windSpeed = bytesToLong(Arrays.copyOfRange(payload, 20, 22));
|
long rawWindSpeed = bytesToLong(Arrays.copyOfRange(payload,20,22));
|
||||||
|
|
||||||
currentTimeLong = currentTime;
|
currentTimeLong = currentTime;
|
||||||
DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
|
DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
|
||||||
@@ -215,16 +221,16 @@ public class StreamParser extends Thread {
|
|||||||
|
|
||||||
double windDirFactor = 0x4000 / 90; //0x4000 is 90 degrees, 0x8000 is 180 degrees, etc...
|
double windDirFactor = 0x4000 / 90; //0x4000 is 90 degrees, 0x8000 is 180 degrees, etc...
|
||||||
windDirection = windDir / windDirFactor;
|
windDirection = windDir / windDirFactor;
|
||||||
|
windSpeed = rawWindSpeed / 1000 * MS_TO_KNOTS;
|
||||||
|
|
||||||
int noBoats = payload[22];
|
int noBoats = payload[22];
|
||||||
int raceType = payload[23];
|
int raceType = payload[23];
|
||||||
boatsPos = new TreeMap<>();
|
|
||||||
for (int i = 0; i < noBoats; i++) {
|
for (int i = 0; i < noBoats; i++) {
|
||||||
long boatStatusSourceID = bytesToLong(
|
long boatStatusSourceID = bytesToLong(
|
||||||
Arrays.copyOfRange(payload, 24 + (i * 20), 28 + (i * 20)));
|
Arrays.copyOfRange(payload, 24 + (i * 20), 28 + (i * 20)));
|
||||||
Yacht boat = boats.get((int) boatStatusSourceID);
|
Yacht boat = boats.get((int) boatStatusSourceID);
|
||||||
boat.setBoatStatus((int) payload[28 + (i * 20)]);
|
boat.setBoatStatus((int) payload[28 + (i * 20)]);
|
||||||
boat.setLegNumber((int) payload[29 + (i * 20)]);
|
setBoatLegPosition(boat, (int) payload[29 + (i * 20)]);
|
||||||
boat.setPenaltiesAwarded((int) payload[30 + (i * 20)]);
|
boat.setPenaltiesAwarded((int) payload[30 + (i * 20)]);
|
||||||
boat.setPenaltiesServed((int) payload[31 + (i * 20)]);
|
boat.setPenaltiesServed((int) payload[31 + (i * 20)]);
|
||||||
Long estTimeAtNextMark = bytesToLong(
|
Long estTimeAtNextMark = bytesToLong(
|
||||||
@@ -233,6 +239,7 @@ public class StreamParser extends Thread {
|
|||||||
Long estTimeAtFinish = bytesToLong(
|
Long estTimeAtFinish = bytesToLong(
|
||||||
Arrays.copyOfRange(payload, 38 + (i * 20), 44 + (i * 20)));
|
Arrays.copyOfRange(payload, 38 + (i * 20), 44 + (i * 20)));
|
||||||
boat.setEstimateTimeAtFinish(estTimeAtFinish);
|
boat.setEstimateTimeAtFinish(estTimeAtFinish);
|
||||||
|
// boatsPos.put(estTimeAtFinish, boat);
|
||||||
// String boatStatus = "SourceID: " + boatStatusSourceID;
|
// String boatStatus = "SourceID: " + boatStatusSourceID;
|
||||||
// boatStatus += "\nBoat Status: " + (int)payload[28 + (i * 20)];
|
// boatStatus += "\nBoat Status: " + (int)payload[28 + (i * 20)];
|
||||||
// boatStatus += "\nLegNumber: " + (int)payload[29 + (i * 20)];
|
// boatStatus += "\nLegNumber: " + (int)payload[29 + (i * 20)];
|
||||||
@@ -242,17 +249,35 @@ public class StreamParser extends Thread {
|
|||||||
// boatStatus += "\nEstTimeAtFinish: " + bytesToLong(Arrays.copyOfRange(payload,37 + (i * 20),43+ (i * 20)));
|
// boatStatus += "\nEstTimeAtFinish: " + bytesToLong(Arrays.copyOfRange(payload,37 + (i * 20),43+ (i * 20)));
|
||||||
// boatStatuses.add(boatStatus);
|
// boatStatuses.add(boatStatus);
|
||||||
}
|
}
|
||||||
if (isRaceStarted()) {
|
// if (isRaceStarted()) {
|
||||||
int pos = 1;
|
// int pos = 1;
|
||||||
for (Yacht yacht : boatsPos.values()) {
|
// for (Yacht yacht : boatsPos.values()) {
|
||||||
yacht.setPosition(String.valueOf(pos));
|
// yacht.setPosition(String.valueOf(pos));
|
||||||
pos++;
|
// pos++;
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// for (Yacht yacht : boatsPos.values()) {
|
||||||
|
// yacht.setPosition("-");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
for (Yacht yacht : boatsPos.values()) {
|
private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){
|
||||||
yacht.setPosition("-");
|
Integer placing = 1;
|
||||||
|
if (leg != updatingBoat.getLegNumber() && (raceStarted || raceFinished)) {
|
||||||
|
for (Yacht boat : boats.values()) {
|
||||||
|
if (boat.getLegNumber() != null && leg <= boat.getLegNumber()){
|
||||||
|
placing += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
updatingBoat.setPosition(placing.toString());
|
||||||
|
updatingBoat.setLegNumber(leg);
|
||||||
|
boatsPos.putIfAbsent(placing, updatingBoat);
|
||||||
|
boatsPos.replace(placing, updatingBoat);
|
||||||
|
} else if(updatingBoat.getLegNumber() == null){
|
||||||
|
updatingBoat.setPosition("1");
|
||||||
|
updatingBoat.setLegNumber(leg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -395,9 +420,9 @@ public class StreamParser extends Thread {
|
|||||||
BoatPositionPacket boatPacket = new BoatPositionPacket(boatId, timeValid, lat, lon,
|
BoatPositionPacket boatPacket = new BoatPositionPacket(boatId, timeValid, lat, lon,
|
||||||
heading, groundSpeed);
|
heading, groundSpeed);
|
||||||
|
|
||||||
//add a new priority que to the boatPositions HashMap
|
//add a new priority que to the boatLocations HashMap
|
||||||
if (!boatPositions.containsKey(boatId)) {
|
if (!boatLocations.containsKey(boatId)) {
|
||||||
boatPositions.put(boatId,
|
boatLocations.put(boatId,
|
||||||
new PriorityBlockingQueue<>(256, new Comparator<BoatPositionPacket>() {
|
new PriorityBlockingQueue<>(256, new Comparator<BoatPositionPacket>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(BoatPositionPacket p1, BoatPositionPacket p2) {
|
public int compare(BoatPositionPacket p1, BoatPositionPacket p2) {
|
||||||
@@ -405,14 +430,14 @@ public class StreamParser extends Thread {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
boatPositions.get(boatId).put(boatPacket);
|
boatLocations.get(boatId).put(boatPacket);
|
||||||
} else if (deviceType == 3) {
|
} else if (deviceType == 3) {
|
||||||
BoatPositionPacket markPacket = new BoatPositionPacket(boatId, timeValid, lat, lon,
|
BoatPositionPacket markPacket = new BoatPositionPacket(boatId, timeValid, lat, lon,
|
||||||
heading, groundSpeed);
|
heading, groundSpeed);
|
||||||
|
|
||||||
//add a new priority que to the boatPositions HashMap
|
//add a new priority que to the boatLocations HashMap
|
||||||
if (!markPositions.containsKey(boatId)) {
|
if (!markLocations.containsKey(boatId)) {
|
||||||
markPositions.put(boatId,
|
markLocations.put(boatId,
|
||||||
new PriorityBlockingQueue<>(256, new Comparator<BoatPositionPacket>() {
|
new PriorityBlockingQueue<>(256, new Comparator<BoatPositionPacket>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(BoatPositionPacket p1, BoatPositionPacket p2) {
|
public int compare(BoatPositionPacket p1, BoatPositionPacket p2) {
|
||||||
@@ -420,7 +445,7 @@ public class StreamParser extends Thread {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
markPositions.get(boatId).put(markPacket);
|
markLocations.get(boatId).put(markPacket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,6 +467,12 @@ public class StreamParser extends Thread {
|
|||||||
|
|
||||||
// assign mark rounding time to boat
|
// assign mark rounding time to boat
|
||||||
boats.get((int)subjectId).setMarkRoundingTime(timeStamp);
|
boats.get((int)subjectId).setMarkRoundingTime(timeStamp);
|
||||||
|
|
||||||
|
for (Mark mark : xmlObject.getRaceXML().getAllCompoundMarks()) {
|
||||||
|
if (mark.getCompoundMarkID() == markId) {
|
||||||
|
boats.get((int)subjectId).setLastMarkRounded(mark);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -578,6 +609,15 @@ public class StreamParser extends Thread {
|
|||||||
return windDirection;
|
return windDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the wind speed in knots
|
||||||
|
* @return A double indicating the wind speed in knots
|
||||||
|
*/
|
||||||
|
public static Double getWindSpeed() {
|
||||||
|
return windSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns stream time in formatted string format
|
* returns stream time in formatted string format
|
||||||
*
|
*
|
||||||
@@ -592,7 +632,8 @@ public class StreamParser extends Thread {
|
|||||||
*
|
*
|
||||||
* @return a map of time to finish and boat.
|
* @return a map of time to finish and boat.
|
||||||
*/
|
*/
|
||||||
public static Map<Long, Yacht> getBoatsPos() {
|
public static Map<Integer, Yacht> getBoatsPos() {
|
||||||
|
|
||||||
return boatsPos;
|
return boatsPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -235,7 +235,8 @@ public class XMLParser {
|
|||||||
|
|
||||||
//Non atomic race attributes
|
//Non atomic race attributes
|
||||||
private ArrayList<Participant> participants;
|
private ArrayList<Participant> participants;
|
||||||
private ArrayList<Mark> course;
|
private ArrayList<Mark> allMarks;
|
||||||
|
private ArrayList<Mark> nonDuplicateMarks;
|
||||||
private ArrayList<Corner> compoundMarkSequence;
|
private ArrayList<Corner> compoundMarkSequence;
|
||||||
private ArrayList<Limit> courseLimit;
|
private ArrayList<Limit> courseLimit;
|
||||||
|
|
||||||
@@ -283,7 +284,9 @@ public class XMLParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Course
|
//Course
|
||||||
course = createCompoundMarks(docEle);
|
allMarks = new ArrayList<>();
|
||||||
|
nonDuplicateMarks = new ArrayList<>();
|
||||||
|
createCompoundMarks(docEle);
|
||||||
|
|
||||||
//Course Mark Sequence
|
//Course Mark Sequence
|
||||||
compoundMarkSequence = new ArrayList<>();
|
compoundMarkSequence = new ArrayList<>();
|
||||||
@@ -312,27 +315,23 @@ public class XMLParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private ArrayList<Mark> createCompoundMarks(Element docEle) {
|
private void createCompoundMarks(Element docEle) {
|
||||||
ArrayList<Mark> cMarks = new ArrayList<>();
|
|
||||||
|
|
||||||
NodeList cMarkList = docEle.getElementsByTagName("Course").item(0).getChildNodes();
|
NodeList cMarkList = docEle.getElementsByTagName("Course").item(0).getChildNodes();
|
||||||
for (int i = 0; i < cMarkList.getLength(); i++) {
|
for (int i = 0; i < cMarkList.getLength(); i++) {
|
||||||
Node cMarkNode = cMarkList.item(i);
|
Node cMarkNode = cMarkList.item(i);
|
||||||
if (cMarkNode.getNodeName().equals("CompoundMark")) {
|
if (cMarkNode.getNodeName().equals("CompoundMark")) {
|
||||||
Mark mark = createMark(cMarkNode);
|
createAndAddMark(cMarkNode);
|
||||||
if (mark != null) {
|
|
||||||
cMarks.add(mark);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cMarks;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private void createAndAddMark(Node compoundMark) {
|
||||||
|
|
||||||
private Mark createMark(Node compoundMark) {
|
Boolean markSeen = false;
|
||||||
|
|
||||||
List<SingleMark> marksList = new ArrayList<>();
|
List<SingleMark> marksList = new ArrayList<>();
|
||||||
|
Integer compoundMarkID = getNodeAttributeInt(compoundMark, "CompoundMarkID");
|
||||||
String cMarkName = getNodeAttributeString(compoundMark, "Name");
|
String cMarkName = getNodeAttributeString(compoundMark, "Name");
|
||||||
|
|
||||||
NodeList childMarks = compoundMark.getChildNodes();
|
NodeList childMarks = compoundMark.getChildNodes();
|
||||||
@@ -346,27 +345,33 @@ public class XMLParser {
|
|||||||
Double targetLat = getNodeAttributeDouble(markNode, "TargetLat");
|
Double targetLat = getNodeAttributeDouble(markNode, "TargetLat");
|
||||||
Double targetLng = getNodeAttributeDouble(markNode, "TargetLng");
|
Double targetLng = getNodeAttributeDouble(markNode, "TargetLng");
|
||||||
|
|
||||||
SingleMark mark = new SingleMark(markName, targetLat, targetLng, sourceID);
|
SingleMark mark = new SingleMark(markName, targetLat, targetLng, sourceID, compoundMarkID);
|
||||||
marksList.add(mark);
|
marksList.add(mark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (SingleMark mark : marksList) {
|
for (SingleMark mark : marksList) {
|
||||||
if (seenSourceIDs.contains(mark.getId())) {
|
if (seenSourceIDs.contains(mark.getId())) {
|
||||||
return null;
|
markSeen = true;
|
||||||
} else {
|
} else {
|
||||||
seenSourceIDs.add(mark.getId());
|
seenSourceIDs.add(mark.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (marksList.size() == 1) {
|
if (marksList.size() == 1) {
|
||||||
return marksList.get(0);
|
if (!markSeen) {
|
||||||
|
nonDuplicateMarks.add(marksList.get(0));
|
||||||
|
}
|
||||||
|
allMarks.add(marksList.get(0));
|
||||||
} else if (marksList.size() == 2) {
|
} else if (marksList.size() == 2) {
|
||||||
return new GateMark(cMarkName, MarkType.OPEN_GATE, marksList.get(0),
|
GateMark thisGateMark = new GateMark(cMarkName, MarkType.OPEN_GATE, marksList.get(0),
|
||||||
marksList.get(1), marksList.get(0).getLatitude(),
|
marksList.get(1), marksList.get(0).getLatitude(),
|
||||||
marksList.get(0).getLongitude());
|
marksList.get(0).getLongitude(), compoundMarkID);
|
||||||
} else {
|
if(!markSeen) {
|
||||||
return null;
|
nonDuplicateMarks.add(thisGateMark);
|
||||||
|
}
|
||||||
|
allMarks.add(thisGateMark);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -395,8 +400,18 @@ public class XMLParser {
|
|||||||
return participants;
|
return participants;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<Mark> getCompoundMarks() {
|
/**
|
||||||
return course;
|
* @return Returns ALL compound marks as stated in the RaceXML (INCLUDING DUPLICATE MARKS)
|
||||||
|
*/
|
||||||
|
public List<Mark> getAllCompoundMarks() {
|
||||||
|
return allMarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns Marks from the race XML without any duplicates
|
||||||
|
*/
|
||||||
|
public List<Mark> getNonDupCompoundMarks() {
|
||||||
|
return nonDuplicateMarks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<Corner> getCompoundMarkSequence() {
|
public ArrayList<Corner> getCompoundMarkSequence() {
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
Tws,Twa0,Bsp0,Twa1,Bsp1,UpTwa,UpBsp,Twa2,Bsp2,Twa3,Bsp3,Twa4,Bsp4,Twa5,Bsp5,Twa6,Bsp6,DnTwa,DnBsp,Twa7,Bsp7
|
||||||
|
4,0,0,30,4,45,8,60,9,75,10,90,10,115,10,145,10,155,10,175,4
|
||||||
|
8,0,0,30,7,43,10,60,11,75,11,90,11,115,12,145,12,153,12,175,10
|
||||||
|
12,0,0,30,11,43,14.4,60,16,75,20,90,23,115,24,145,23,153,21.6,175,14
|
||||||
|
16,0,0,30,12,42,19.2,60,25,75,27,90,31,115,32,145,30,153,28.8,175,20
|
||||||
|
20,0,0,30,13,41,24,60,29,75,37,90,39,115,40,145,38,153,36,175,24
|
||||||
|
25,0,0,30,15,40,30,60,38,75,44,90,49,115,50,145,49,151,47,175,30
|
||||||
|
30,0,0,30,15,42,30,60,37,75,42,90,48,115,49,145,48,150,46,175,32
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
<Label layoutX="11.0" layoutY="14.0" text="Timer" textFill="WHITE" />
|
<Label layoutX="11.0" layoutY="14.0" text="Timer" textFill="WHITE" />
|
||||||
<Label layoutX="11.0" layoutY="88.0" text="Wind direction" textFill="WHITE" />
|
<Label layoutX="11.0" layoutY="88.0" text="Wind direction" textFill="WHITE" />
|
||||||
<Circle fx:id="windBackgroundCircle" blendMode="DARKEN" fill="#3dcdc8" layoutX="110.0" layoutY="166.0" radius="35.0" stroke="#d7d7d7" strokeType="INSIDE" strokeWidth="3.0" />
|
<Circle fx:id="windBackgroundCircle" blendMode="DARKEN" fill="#3dcdc8" layoutX="110.0" layoutY="166.0" radius="35.0" stroke="#d7d7d7" strokeType="INSIDE" strokeWidth="3.0" />
|
||||||
<Text fx:id="windArrowText" fill="#a8a8a8" layoutX="86.0" layoutY="186.0" strokeType="OUTSIDE" strokeWidth="0.0" text="↑">
|
<Text fx:id="windArrowText" fill="#a8a8a8" layoutX="86.0" layoutY="186.0" strokeType="OUTSIDE" strokeWidth="0.0" text="↓">
|
||||||
<font>
|
<font>
|
||||||
<Font name="AdobeArabic-Regular" size="55.0" />
|
<Font name="AdobeArabic-Regular" size="55.0" />
|
||||||
</font>
|
</font>
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
package seng302;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import seng302.models.Event;
|
|
||||||
import seng302.models.Yacht;
|
|
||||||
import seng302.models.mark.SingleMark;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test for Event class
|
|
||||||
* Created by Haoming on 7/03/17.
|
|
||||||
*/
|
|
||||||
public class EventTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getTimeString() throws Exception {
|
|
||||||
Yacht boat = new Yacht("testBoat");
|
|
||||||
Event event = new Event(1231242.2, boat, new SingleMark("mark1"), new SingleMark("mark2"), 0);
|
|
||||||
assertEquals("20:31:242", event.getTimeString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBoatHeading() throws Exception {
|
|
||||||
Yacht boat = new Yacht("testBoat");
|
|
||||||
Event event = new Event(1231242.2, boat, new SingleMark("mark1", 142.5, 122.1, 1), new SingleMark("mark2", 121.9,99.2, 2), 0);
|
|
||||||
|
|
||||||
assertEquals(event.getBoatHeading(), 228.0266137055349, 1e-15);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDistanceBetweenMarks() throws Exception {
|
|
||||||
Yacht boat = new Yacht("testBoat");
|
|
||||||
Event event = new Event(1231242.2, boat, new SingleMark("mark1", 142.5, 122.1, 1), new SingleMark("mark2", 121.9,99.2, 2), 0);
|
|
||||||
|
|
||||||
assertEquals(event.getDistanceBetweenMarks(), 339059.653830461, 1e-15);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package seng302;
|
||||||
|
|
||||||
|
import javafx.geometry.Point2D;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Class for the GeometryUtils class
|
||||||
|
* Created by wmu16 on 24/05/17.
|
||||||
|
*/
|
||||||
|
public class TestGeoUtils {
|
||||||
|
|
||||||
|
//Line in x = y
|
||||||
|
private Point2D linePoint1 = new Point2D(0, 0);
|
||||||
|
private Point2D linePoint2 = new Point2D(1, 1);
|
||||||
|
|
||||||
|
//Point below x = y
|
||||||
|
private Point2D arbitraryPoint1 = new Point2D(1, 0);
|
||||||
|
|
||||||
|
//Point above x = y
|
||||||
|
private Point2D arbitraryPoint2 = new Point2D(0, 1);
|
||||||
|
|
||||||
|
//Point on x = y
|
||||||
|
private Point2D arbitraryPoint3 = new Point2D(2, 2);
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLineFunction() {
|
||||||
|
|
||||||
|
Integer lineFunctionResult1 = GeometryUtils.lineFunction(linePoint1, linePoint2, arbitraryPoint1);
|
||||||
|
Integer lineFunctionResult2 = GeometryUtils.lineFunction(linePoint1, linePoint2, arbitraryPoint2);
|
||||||
|
Integer lineFunctionResult3 = GeometryUtils.lineFunction(linePoint1, linePoint2, arbitraryPoint3);
|
||||||
|
|
||||||
|
//Point1 and Point2 are on opposite sides
|
||||||
|
assertEquals(Math.abs(lineFunctionResult1), Math.abs(lineFunctionResult2));
|
||||||
|
assertNotEquals(lineFunctionResult1, lineFunctionResult2);
|
||||||
|
|
||||||
|
//Point3 is on the line
|
||||||
|
assertEquals((long) lineFunctionResult3, 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMakeArbitraryVectorPoint() {
|
||||||
|
|
||||||
|
//Make a point (1,0) from point (0,0)
|
||||||
|
Point2D newPoint = GeometryUtils.makeArbitraryVectorPoint(linePoint1, 0d, 1d);
|
||||||
|
Point2D expected = new Point2D(1,0);
|
||||||
|
|
||||||
|
assertEquals(expected.getX(), newPoint.getX(), 1E-6);
|
||||||
|
assertEquals(expected.getY(), newPoint.getY(), 1E-6);
|
||||||
|
|
||||||
|
newPoint = GeometryUtils.makeArbitraryVectorPoint(linePoint1, 90d, 1d);
|
||||||
|
expected = new Point2D(0, 1);
|
||||||
|
|
||||||
|
assertEquals(expected.getX(), newPoint.getX(), 1E-6);
|
||||||
|
assertEquals(expected.getY(), newPoint.getY(), 1E-6);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,9 +16,9 @@ public class MarkTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
this.singleMark1 = new SingleMark("testMark_SM1", 12.23234, -34.342, 1);
|
this.singleMark1 = new SingleMark("testMark_SM1", 12.23234, -34.342, 1, 0);
|
||||||
this.singleMark2 = new SingleMark("testMark_SM2", 12.23239, -34.352, 2);
|
this.singleMark2 = new SingleMark("testMark_SM2", 12.23239, -34.352, 2, 1);
|
||||||
this.gateMark = new GateMark("testMark_GM", MarkType.OPEN_GATE, singleMark1, singleMark2, singleMark1.getLatitude(), singleMark2.getLongitude());
|
this.gateMark = new GateMark("testMark_GM", MarkType.OPEN_GATE, singleMark1, singleMark2, singleMark1.getLatitude(), singleMark2.getLongitude(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user