From e385ac5c09eb840f9182b53c908eb4fc5a10c00b Mon Sep 17 00:00:00 2001 From: William Muir Date: Mon, 22 May 2017 15:39:53 +1200 Subject: [PATCH 01/16] Initial work on static parser class for polar files story[956] --- pom.xml | 6 + src/main/java/seng302/models/PolarTable.java | 116 +++++++++++++++++++ src/main/resources/config/acc_polars.csv | 8 ++ 3 files changed, 130 insertions(+) create mode 100644 src/main/java/seng302/models/PolarTable.java create mode 100644 src/main/resources/config/acc_polars.csv diff --git a/pom.xml b/pom.xml index 8d10c6fc..ea43245e 100644 --- a/pom.xml +++ b/pom.xml @@ -25,6 +25,12 @@ commons-io 1.3.2 + + + com.opencsv + opencsv + 3.9 + com.googlecode.json-simple json-simple diff --git a/src/main/java/seng302/models/PolarTable.java b/src/main/java/seng302/models/PolarTable.java new file mode 100644 index 00000000..578a3a7b --- /dev/null +++ b/src/main/java/seng302/models/PolarTable.java @@ -0,0 +1,116 @@ +package seng302.models; + +import java.io.*; +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> polarTable; + private static HashMap> upwindOptimal; + private static HashMap> 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 thisPolar = new HashMap<>(); + HashMap thisUpWindPolar = new HashMap<>(); + HashMap 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> getPolarTable() { + return polarTable; + } + + + /** + * @return The polar table just containing the optimal upwind values + */ + public static HashMap> getUpwindOptimal() { + return upwindOptimal; + } + + + /** + * @return The polar table just containing the optimal downwind values + */ + public static HashMap> getDownwindOptimal() { + return downwindOptimal; + } + +} \ No newline at end of file diff --git a/src/main/resources/config/acc_polars.csv b/src/main/resources/config/acc_polars.csv new file mode 100644 index 00000000..ee7ea80e --- /dev/null +++ b/src/main/resources/config/acc_polars.csv @@ -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 From 08eacacfd48dee50a366400c34f0d29e688268d2 Mon Sep 17 00:00:00 2001 From: William Muir Date: Mon, 22 May 2017 15:45:16 +1200 Subject: [PATCH 02/16] Polar table is now parsed form the stored file in resources on startup in main story[956] --- src/main/java/seng302/App.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index 5c595ccf..b5838b8b 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -5,6 +5,7 @@ import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; +import seng302.models.PolarTable; import seng302.models.stream.StreamParser; import seng302.models.stream.StreamReceiver; import seng302.server.ServerThread; @@ -13,6 +14,8 @@ public class App extends Application { @Override 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")); primaryStage.setTitle("RaceVision"); primaryStage.setScene(new Scene(root)); @@ -62,7 +65,7 @@ public class App extends Application { } } //Change the StreamReceiver in this else block to change the default data source. - else{ + else { // sr = new StreamReceiver("localhost", 4949, "RaceStream"); sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream"); // sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream"); @@ -73,7 +76,6 @@ public class App extends Application { streamParser.start(); launch(args); - } } From e1b8e199668ac054efaed6b5ed074c71448b1505 Mon Sep 17 00:00:00 2001 From: William Muir Date: Mon, 22 May 2017 18:54:07 +1200 Subject: [PATCH 03/16] Initial work on finding next marks for boats as from mark rounding messages Marks were very difficult to extend, need large refactor Marks now have a compound ID as well as their sourceID. They need this compound ID to be identifiable by the mark rounding messages. tags: #story[956] #pair[wmu16, mra106] --- .../seng302/controllers/CanvasController.java | 16 +- .../controllers/RaceViewController.java | 8 + src/main/java/seng302/models/Event.java | 172 ------------------ src/main/java/seng302/models/Leg.java | 116 ------------ src/main/java/seng302/models/Yacht.java | 10 + .../java/seng302/models/mark/GateMark.java | 4 +- src/main/java/seng302/models/mark/Mark.java | 15 +- .../java/seng302/models/mark/SingleMark.java | 17 +- .../seng302/models/stream/StreamParser.java | 7 + .../java/seng302/models/stream/XMLParser.java | 5 +- src/test/java/seng302/EventTest.java | 38 ---- src/test/java/seng302/LegTest.java | 54 ------ .../java/seng302/models/mark/MarkTest.java | 6 +- 13 files changed, 54 insertions(+), 414 deletions(-) delete mode 100644 src/main/java/seng302/models/Event.java delete mode 100644 src/main/java/seng302/models/Leg.java delete mode 100644 src/test/java/seng302/EventTest.java delete mode 100644 src/test/java/seng302/LegTest.java diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 0bdeae25..1b2ea66d 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -154,9 +154,9 @@ public class CanvasController { double[] yBoundaryPoints = new double[courseLimits.size()]; for (int i = 0; i < courseLimits.size() - 1; 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); - 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 borderPoint2 = findScaledXY(thisMark2); gc.strokeLine(borderPoint1.getX(), borderPoint1.getY(), @@ -165,9 +165,9 @@ public class CanvasController { yBoundaryPoints[i] = borderPoint1.getY(); } 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); - 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 borderPoint2 = findScaledXY(thisMark2); gc.strokeLine(borderPoint1.getX(), borderPoint1.getY(), @@ -355,15 +355,15 @@ public class CanvasController { sortedPoints.sort(Comparator.comparingDouble(Limit::getLat)); Limit minLatMark = sortedPoints.get(0); Limit maxLatMark = sortedPoints.get(sortedPoints.size()-1); - minLatPoint = new SingleMark(minLatMark.toString(), minLatMark.getLat(), minLatMark.getLng(), minLatMark.getSeqID()); - maxLatPoint = new SingleMark(maxLatMark.toString(), maxLatMark.getLat(), maxLatMark.getLng(), maxLatMark.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(), minLatMark.getSeqID()); sortedPoints.sort(Comparator.comparingDouble(Limit::getLng)); //If the course is on a point on the earth where longitudes wrap around. Limit minLonMark = sortedPoints.get(0); Limit maxLonMark = sortedPoints.get(sortedPoints.size()-1); - minLonPoint = new SingleMark(minLonMark.toString(), minLonMark.getLat(), minLonMark.getLng(), minLonMark.getSeqID()); - maxLonPoint = new SingleMark(maxLonMark.toString(), maxLonMark.getLat(), maxLonMark.getLng(), maxLonMark.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(), minLonMark.getSeqID()); if (maxLonPoint.getLongitude() - minLonPoint.getLongitude() > 180) { horizontalInversion = true; } diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index 43c04a9b..5c589004 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -183,6 +183,14 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel updateOrder(); updateBoatSelectionComboBox(); + for (Yacht yacht : StreamParser.getBoatsPos().values()) { + if (yacht.getLastMarkRounded() != null) { + System.out.println(yacht.getLastMarkRounded().getName()); + } else { + System.out.println("sup"); + } + } + }) ); diff --git a/src/main/java/seng302/models/Event.java b/src/main/java/seng302/models/Event.java deleted file mode 100644 index 80f688c7..00000000 --- a/src/main/java/seng302/models/Event.java +++ /dev/null @@ -1,172 +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"); - } -// System.out.println(this.getDistanceBetweenMarks()); - 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; - } -} \ No newline at end of file diff --git a/src/main/java/seng302/models/Leg.java b/src/main/java/seng302/models/Leg.java deleted file mode 100644 index 8f21a6ea..00000000 --- a/src/main/java/seng302/models/Leg.java +++ /dev/null @@ -1,116 +0,0 @@ -package seng302.models; - -import seng302.models.mark.SingleMark; - -/** -* Represents the leg of a race. -*/ -public class Leg { - private int heading; - private int distance; - private boolean isFinishingLeg; - private SingleMark startingSingleMark; - - /** - * Create a new leg - * - * @param heading, the magnetic heading of this leg - * @param distance, the total distance of this leg in meters - * @param singleMark, the singleMark this leg starts on - */ - public Leg(int heading, int distance, SingleMark singleMark) { - this.heading = heading; - this.distance = distance; - this.startingSingleMark = singleMark; - this.isFinishingLeg = false; - } - - /** - * Create a new leg - * - * @param heading, the magnetic heading of this leg - * @param distance, the total distance of this leg in meters - * @param markerName, the name of the marker this leg starts on - */ - public Leg(int heading, int distance, String markerName) { - this.heading = heading; - this.distance = distance; - this.startingSingleMark = new SingleMark(markerName); - this.isFinishingLeg = false; - } - - /** - * Get the heading of this leg - * @return int - */ - public int getHeading() { - return this.heading; - } - - /** - * Set the heading for this leg - * @param heading - */ - public void setHeading(int heading) { - this.heading = heading; - } - - /** - * Get the total distance of this leg in meters - * @return int - */ - public int getDistance() { - return this.distance; - } - - /** - * Set the distance of this leg in meters - * @param distance - */ - public void setDistance(int distance) { - this.distance = distance; - } - - /** - * Returns the marker this leg started on - * @return SingleMark - */ - public SingleMark getMarker() { - return this.startingSingleMark; - } - - /** - * Set the singleMark this leg starts on - * @param singleMark - */ - public void setMarker(SingleMark singleMark) { - this.startingSingleMark = singleMark; - } - - /** - * Returns the name of the marker this leg started on - * @return String - */ - public String getMarkerLabel() { - return this.startingSingleMark.getName(); - } - - - - /** - * Specify whether or not the race finishes on this leg - * - * @param isFinishingLeg whether or not the race finishes on this leg - */ - public void setFinishingLeg(boolean isFinishingLeg) { - this.isFinishingLeg = isFinishingLeg; - } - - /** - * Returns whether or not the race finishes after this leg - * @return true if this the race finishes after this leg - */ - public boolean getIsFinishingLeg() { - return this.isFinishingLeg; - } -} \ No newline at end of file diff --git a/src/main/java/seng302/models/Yacht.java b/src/main/java/seng302/models/Yacht.java index 8ae5d76c..a5899540 100644 --- a/src/main/java/seng302/models/Yacht.java +++ b/src/main/java/seng302/models/Yacht.java @@ -1,6 +1,7 @@ package seng302.models; import javafx.scene.paint.Color; +import seng302.models.mark.Mark; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -32,6 +33,7 @@ public class Yacht { private String position; // Mark rounding private Long markRoundingTime; + private Mark lastMarkRounded; /** * Used in EventTest and RaceTest. @@ -167,6 +169,14 @@ public class Yacht { this.markRoundingTime = markRoundingTime; } + public Mark getLastMarkRounded() { + return lastMarkRounded; + } + + public void setLastMarkRounded(Mark lastMarkRounded) { + this.lastMarkRounded = lastMarkRounded; + } + @Override public String toString() { return boatName; diff --git a/src/main/java/seng302/models/mark/GateMark.java b/src/main/java/seng302/models/mark/GateMark.java index 79943114..8459b882 100644 --- a/src/main/java/seng302/models/mark/GateMark.java +++ b/src/main/java/seng302/models/mark/GateMark.java @@ -16,8 +16,8 @@ public class GateMark extends Mark { * @param singleMark1 one single 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) { - super(name, type, latitude, longitude); + public GateMark(String name, MarkType type, SingleMark singleMark1, SingleMark singleMark2, double latitude, double longitude, int compoundMarkID) { + super(name, type, latitude, longitude, compoundMarkID); this.singleMark1 = singleMark1; this.singleMark2 = singleMark2; } diff --git a/src/main/java/seng302/models/mark/Mark.java b/src/main/java/seng302/models/mark/Mark.java index 1ca1f608..31791d99 100644 --- a/src/main/java/seng302/models/mark/Mark.java +++ b/src/main/java/seng302/models/mark/Mark.java @@ -11,24 +11,27 @@ public abstract class Mark { private double latitude; private double longitude; private long id; + private int compoundMarkID; /** * Create a mark instance by passing its name and type * @param name the name of the 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.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.markType = markType; this.latitude = latitude; this.longitude = longitude; - id = 0; + this.id = 0; + this.compoundMarkID = compoundMarkID; } /** @@ -132,4 +135,8 @@ public abstract class Mark { public void setId(int id) { this.id = id; } + + public int getCompoundMarkID() { + return compoundMarkID; + } } diff --git a/src/main/java/seng302/models/mark/SingleMark.java b/src/main/java/seng302/models/mark/SingleMark.java index 9239552a..56ba9dc6 100644 --- a/src/main/java/seng302/models/mark/SingleMark.java +++ b/src/main/java/seng302/models/mark/SingleMark.java @@ -9,7 +9,6 @@ public class SingleMark extends Mark { private double lat; private double lon; private String name; - private int id; /** * Represents a marker @@ -18,24 +17,12 @@ public class SingleMark extends Mark { * @param lat, the latitude of the marker * @param lon, the longitude of the marker */ - public SingleMark(String name, double lat, double lon, int id) { - super(name, MarkType.SINGLE_MARK, id); + public SingleMark(String name, double lat, double lon, int sourceID, int compoundMarkID) { + super(name, MarkType.SINGLE_MARK, sourceID, compoundMarkID); this.lat = lat; 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() { return this.lat; diff --git a/src/main/java/seng302/models/stream/StreamParser.java b/src/main/java/seng302/models/stream/StreamParser.java index f800379c..f31a0519 100644 --- a/src/main/java/seng302/models/stream/StreamParser.java +++ b/src/main/java/seng302/models/stream/StreamParser.java @@ -22,6 +22,7 @@ import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import seng302.models.Yacht; +import seng302.models.mark.Mark; import seng302.models.stream.packets.BoatPositionPacket; import seng302.models.stream.packets.StreamPacket; @@ -441,6 +442,12 @@ public class StreamParser extends Thread{ // assign mark rounding time to boat boats.get((int)subjectId).setMarkRoundingTime(timeStamp); + + for (Mark mark : xmlObject.getRaceXML().getCompoundMarks()) { + if (mark.getCompoundMarkID() == markId) { + boats.get((int)subjectId).setLastMarkRounded(mark); + } + } } /** diff --git a/src/main/java/seng302/models/stream/XMLParser.java b/src/main/java/seng302/models/stream/XMLParser.java index 674a2611..4bda96cf 100644 --- a/src/main/java/seng302/models/stream/XMLParser.java +++ b/src/main/java/seng302/models/stream/XMLParser.java @@ -333,6 +333,7 @@ public class XMLParser { private Mark createMark(Node compoundMark) { List marksList = new ArrayList<>(); + Integer compoundMarkID = getNodeAttributeInt(compoundMark, "CompoundMarkID"); String cMarkName = getNodeAttributeString(compoundMark, "Name"); NodeList childMarks = compoundMark.getChildNodes(); @@ -346,7 +347,7 @@ public class XMLParser { Double targetLat = getNodeAttributeDouble(markNode, "TargetLat"); 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); } } @@ -364,7 +365,7 @@ public class XMLParser { } else if (marksList.size() == 2) { return new GateMark(cMarkName, MarkType.OPEN_GATE, marksList.get(0), marksList.get(1), marksList.get(0).getLatitude(), - marksList.get(0).getLongitude()); + marksList.get(0).getLongitude(), compoundMarkID); } else { return null; } diff --git a/src/test/java/seng302/EventTest.java b/src/test/java/seng302/EventTest.java deleted file mode 100644 index 4c98ddd6..00000000 --- a/src/test/java/seng302/EventTest.java +++ /dev/null @@ -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); - } -} \ No newline at end of file diff --git a/src/test/java/seng302/LegTest.java b/src/test/java/seng302/LegTest.java deleted file mode 100644 index 9bb64b6c..00000000 --- a/src/test/java/seng302/LegTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package seng302; - -import org.junit.Test; -import seng302.models.Leg; -import seng302.models.mark.SingleMark; - -import static org.junit.Assert.assertEquals; - -/** - * Unit test for the Leg class. - */ -public class LegTest { - - /** - * Test creation of the leg by specifying a string - * for the marker label - */ - @Test - public void testLegCreationUsingMarkerLabel() { - Leg leg = new Leg(010, 100, "SingleMark"); - - assertEquals(leg.getHeading(), 010); - assertEquals(leg.getDistance(), 100); - assertEquals(leg.getMarkerLabel(), "SingleMark"); - assertEquals(leg.getIsFinishingLeg(), false); - } - - /** - * Test creation of the leg by providing a - * SingleMark object - */ - @Test - public void testLegCreation() { - Leg leg = new Leg(010, 100, new SingleMark("SingleMark")); - - assertEquals(leg.getHeading(), 010); - assertEquals(leg.getDistance(), 100); - assertEquals(leg.getMarkerLabel(), "SingleMark"); - assertEquals(leg.getIsFinishingLeg(), false); - } - - /** - * Test changing whether or not a - * leg is the finishing leg - */ - @Test - public void testSetFinishLeg() { - Leg leg = new Leg(010, 100, "SingleMark"); - - leg.setFinishingLeg(true); - assertEquals(leg.getIsFinishingLeg(), true); - } - -} diff --git a/src/test/java/seng302/models/mark/MarkTest.java b/src/test/java/seng302/models/mark/MarkTest.java index 3db92540..8615be5f 100644 --- a/src/test/java/seng302/models/mark/MarkTest.java +++ b/src/test/java/seng302/models/mark/MarkTest.java @@ -16,9 +16,9 @@ public class MarkTest { @Before public void setUp() throws Exception { - this.singleMark1 = new SingleMark("testMark_SM1", 12.23234, -34.342, 1); - this.singleMark2 = new SingleMark("testMark_SM2", 12.23239, -34.352, 2); - this.gateMark = new GateMark("testMark_GM", MarkType.OPEN_GATE, singleMark1, singleMark2, singleMark1.getLatitude(), singleMark2.getLongitude()); + this.singleMark1 = new SingleMark("testMark_SM1", 12.23234, -34.342, 1, 0); + 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(), 2); } @Test From ca8ea038701a2c39c95a443f2d8bfb867a3ab79b Mon Sep 17 00:00:00 2001 From: Michael Rausch Date: Tue, 23 May 2017 18:54:42 +1200 Subject: [PATCH 04/16] Added method in mark group to draw lay lines, also added next mark to Yacht class. #story[956] --- .../controllers/RaceViewController.java | 7 ++++ src/main/java/seng302/models/Yacht.java | 10 ++++++ .../java/seng302/models/mark/MarkGroup.java | 32 +++++++++++++++++++ .../seng302/models/stream/StreamParser.java | 19 ++++++++++- 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index 5c589004..1f7ae19f 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -184,11 +184,18 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel updateBoatSelectionComboBox(); for (Yacht yacht : StreamParser.getBoatsPos().values()) { + System.out.println("\n\nyacht.getBoatName() = " + yacht.getBoatName()); + System.out.println("yacht.getLastMarkRounded() = " + yacht.getLastMarkRounded()); + System.out.println("yacht.getNextMark() = " + yacht.getNextMark()); + if (yacht.getLastMarkRounded() != null) { System.out.println(yacht.getLastMarkRounded().getName()); } else { System.out.println("sup"); } + if (yacht.getNextMark() != null){ + System.out.println("yacht = " + yacht.getNextMark().getName()); + } } }) diff --git a/src/main/java/seng302/models/Yacht.java b/src/main/java/seng302/models/Yacht.java index a5899540..88287ba8 100644 --- a/src/main/java/seng302/models/Yacht.java +++ b/src/main/java/seng302/models/Yacht.java @@ -5,6 +5,7 @@ import seng302.models.mark.Mark; import java.text.DateFormat; import java.text.SimpleDateFormat; +import seng302.models.stream.XMLParser.RaceXMLObject.Corner; /** * Yacht class for the racing boat. @@ -34,6 +35,7 @@ public class Yacht { // Mark rounding private Long markRoundingTime; private Mark lastMarkRounded; + private Mark nextMark; /** * Used in EventTest and RaceTest. @@ -181,4 +183,12 @@ public class Yacht { public String toString() { return boatName; } + + public void setNextMark(Mark nextMark) { + this.nextMark = nextMark; + } + + public Mark getNextMark(){ + return nextMark; + } } diff --git a/src/main/java/seng302/models/mark/MarkGroup.java b/src/main/java/seng302/models/mark/MarkGroup.java index c87ae174..f7327107 100644 --- a/src/main/java/seng302/models/mark/MarkGroup.java +++ b/src/main/java/seng302/models/mark/MarkGroup.java @@ -45,6 +45,36 @@ public class MarkGroup extends Group { super.getChildren().add(markCircle); } + 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); + } + + /** + * Adds a lay-line to the MarkGroup + * @param startPoint The mark where the lay line starts + * @param layLineAngle The angle the laylines point + * @param baseAngle The reference angle + */ + private void addLayLine(Point2D startPoint, Double layLineAngle, Double baseAngle){ + + Point2D ep1 = getPointRotation(startPoint, 50.0, baseAngle + -layLineAngle); + Point2D ep2 = getPointRotation(startPoint, 50.0, baseAngle + layLineAngle); + + Line line1 = new Line(startPoint.getX(), startPoint.getY(), ep1.getX(), ep1.getY()); + Line line2 = new Line(startPoint.getX(), startPoint.getY(), ep2.getX(), ep2.getY()); + + line1.setStrokeWidth(0.5); + line1.setStroke(Color.GREEN); + + line2.setStrokeWidth(0.5); + line2.setStroke(Color.GREEN); + + super.getChildren().addAll(line1, line2); + } + public MarkGroup(GateMark mark, Point2D points1, Point2D points2) { marks.add(mark.getSingleMark1()); marks.add(mark.getSingleMark2()); @@ -84,6 +114,8 @@ public class MarkGroup extends Group { } super.getChildren().add(line); + addLayLine(points1, 12.0, 90.0); + addLayLine(points2, 12.0, 90.0); } public void moveMarkTo (double x, double y, long raceId) diff --git a/src/main/java/seng302/models/stream/StreamParser.java b/src/main/java/seng302/models/stream/StreamParser.java index f31a0519..00bfed25 100644 --- a/src/main/java/seng302/models/stream/StreamParser.java +++ b/src/main/java/seng302/models/stream/StreamParser.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; +import java.util.List; import java.util.Map; import java.util.TimeZone; import java.util.TreeMap; @@ -23,6 +24,7 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; import seng302.models.Yacht; import seng302.models.mark.Mark; +import seng302.models.stream.XMLParser.RaceXMLObject.Corner; import seng302.models.stream.packets.BoatPositionPacket; import seng302.models.stream.packets.StreamPacket; @@ -446,7 +448,22 @@ public class StreamParser extends Thread{ for (Mark mark : xmlObject.getRaceXML().getCompoundMarks()) { if (mark.getCompoundMarkID() == markId) { boats.get((int)subjectId).setLastMarkRounded(mark); - } + + List markSequence = xmlObject.getRaceXML().getCompoundMarkSequence(); + + for (int i = 0; i < markSequence.size() - 1; i++){ + Corner corner = markSequence.get(i); + + if (corner.getCompoundMarkID().equals(mark.getCompoundMarkID()) && (i + 1) < markSequence.size()){ + Corner nextCorner = markSequence.get(i+1); + for (Mark m : xmlObject.getRaceXML().getCompoundMarks()){ + if (m.getCompoundMarkID() == nextCorner.getCompoundMarkID()){ + boats.get((int)subjectId).setNextMark(m); + } + } + } + } + } } } From 89464e033efea44ef44ee21d1041f709e32bf1bc Mon Sep 17 00:00:00 2001 From: William Muir Date: Wed, 24 May 2017 00:08:55 +1200 Subject: [PATCH 05/16] Initial work on calculating when a gate is upwind or downwind Need to know if a gate is upwind or downwind to use the appropriate polar table Currently calculate the angle between the next mark and the vector of the current mark to the wind, if this angle is less than 90 degrees than the next mark should be down wind Pretty poor implementation currently, just prototype Doesn't appear to be working as intended currently. Just a prototype for how we could implement further tags: #story[956] --- .../controllers/RaceViewController.java | 14 ++++----- src/main/java/seng302/models/BoatGroup.java | 31 +++++++++++++++++++ .../java/seng302/models/mark/MarkGroup.java | 3 ++ 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index 1f7ae19f..f1bd50b2 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -184,17 +184,17 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel updateBoatSelectionComboBox(); for (Yacht yacht : StreamParser.getBoatsPos().values()) { - System.out.println("\n\nyacht.getBoatName() = " + yacht.getBoatName()); - System.out.println("yacht.getLastMarkRounded() = " + yacht.getLastMarkRounded()); - System.out.println("yacht.getNextMark() = " + yacht.getNextMark()); if (yacht.getLastMarkRounded() != null) { - System.out.println(yacht.getLastMarkRounded().getName()); - } else { - System.out.println("sup"); + System.out.println("\n\nboat: " + yacht.getBoatName()); + System.out.println("last Mark: " + yacht.getLastMarkRounded().getName()); } if (yacht.getNextMark() != null){ - System.out.println("yacht = " + yacht.getNextMark().getName()); + System.out.println("next Mark: " + yacht.getNextMark().getName()); + for (BoatGroup bg : includedCanvasController.getBoatGroups()) { + bg.calculateLegDirection(); + } + } } diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java index 3e97df5e..6ee1a3d8 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -12,6 +12,7 @@ import javafx.scene.shape.Line; import javafx.scene.shape.Polygon; import javafx.scene.text.Text; import javafx.scene.transform.Rotate; +import seng302.models.mark.Mark; import seng302.models.stream.StreamParser; import java.text.DateFormat; @@ -363,6 +364,36 @@ public class BoatGroup extends Group { } + public void calculateLegDirection() { + Mark lastMark = boat.getLastMarkRounded(); + Mark nextMark = boat.getNextMark(); + if (lastMark == null || nextMark == null) { + return; + } + + Double windDirection = StreamParser.getWindDirection(); + Double arbitraryDistance = 10d; + + Point2D lastMarkMidPoint = new Point2D(lastMark.getLatitude(), lastMark.getLongitude()); + Point2D nextMarkMidPoint = new Point2D(nextMark.getLatitude(), nextMark.getLongitude()); + + Double windDirX = lastMarkMidPoint.getX() + (lastMarkMidPoint.getX() + arbitraryDistance -lastMarkMidPoint.getX())*Math.cos(windDirection) - (lastMarkMidPoint.getY() + arbitraryDistance -lastMarkMidPoint.getY())*Math.sin(windDirection); + Double windDirY = lastMarkMidPoint.getY() + (lastMarkMidPoint.getX() + arbitraryDistance -lastMarkMidPoint.getX())*Math.sin(windDirection) + (lastMarkMidPoint.getY() + arbitraryDistance -lastMarkMidPoint.getY())*Math.cos(windDirection); + Point2D windDirPoint = new Point2D(windDirX, windDirY); + + Double angle = lastMarkMidPoint.angle(nextMarkMidPoint, windDirPoint); + + if (angle <= 90) { + System.out.println(lastMark.getName() + " is downwind"); + System.out.println(nextMark.getName() + " is upwind"); + } + +// if (lastMarkMidPoint.angle(nextMarkMidPoint, windDirPoint) <= 90) { +// boat.getNextMark().s +// } + } + + public void setIsSelected(Boolean isSelected) { this.isSelected = isSelected; setTeamNameObjectVisible(isSelected); diff --git a/src/main/java/seng302/models/mark/MarkGroup.java b/src/main/java/seng302/models/mark/MarkGroup.java index f7327107..a6651434 100644 --- a/src/main/java/seng302/models/mark/MarkGroup.java +++ b/src/main/java/seng302/models/mark/MarkGroup.java @@ -114,6 +114,9 @@ public class MarkGroup extends Group { } super.getChildren().add(line); + //Laylines +// if (mark.) + addLayLine(points1, 12.0, 90.0); addLayLine(points2, 12.0, 90.0); } From 1cac7cc189f00cab0748b003cf5b72059e337d3d Mon Sep 17 00:00:00 2001 From: William Muir Date: Wed, 24 May 2017 14:57:22 +1200 Subject: [PATCH 06/16] Calculation of upwind downwind leg given a boat, wind and next mark now works. created GeometryUtils class Can accurately calculate if a boat is going upwind or downward using a line function for the wind vector from the gate and the boat position from the gate. Requires knowledge of the next mark which requires the boat to have passed a mark. This could be fixed by extracting the leg number from the race status packet and mapping these to gates in an initalisation step tags: #story[956] --- src/main/java/seng302/App.java | 1 + src/main/java/seng302/GeometryUtils.java | 63 ++++++++++++++++++ .../seng302/controllers/CanvasController.java | 6 +- .../controllers/RaceViewController.java | 28 ++++++-- src/main/java/seng302/models/BoatGroup.java | 63 +++++++++++------- .../java/seng302/models/mark/MarkGroup.java | 4 ++ src/test/java/seng302/TestGeoUtils.java | 65 +++++++++++++++++++ 7 files changed, 200 insertions(+), 30 deletions(-) create mode 100644 src/main/java/seng302/GeometryUtils.java create mode 100644 src/test/java/seng302/TestGeoUtils.java diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index ecb9e4a3..c53a4300 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -67,6 +67,7 @@ public class App extends Application { else { // sr = new StreamReceiver("localhost", 4949, "RaceStream"); sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream"); +// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream"); } sr.start(); diff --git a/src/main/java/seng302/GeometryUtils.java b/src/main/java/seng302/GeometryUtils.java new file mode 100644 index 00000000..ce8b9fe4 --- /dev/null +++ b/src/main/java/seng302/GeometryUtils.java @@ -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); + + } + +} diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 1b2ea66d..36d56507 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -435,7 +435,7 @@ public class CanvasController { return findScaledXY (unscaled.getLatitude(), unscaled.getLongitude()); } - private Point2D findScaledXY (double unscaledLat, double unscaledLon) { + public Point2D findScaledXY (double unscaledLat, double unscaledLon) { double distanceFromReference; double angleFromReference; int xAxisLocation = (int) referencePointX; @@ -468,4 +468,8 @@ public class CanvasController { List getBoatGroups() { return boatGroups; } + + List getMarkGroups() { + return markGroups; + } } \ No newline at end of file diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index f1bd50b2..754da2eb 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -26,6 +26,9 @@ import seng302.controllers.annotations.ImportantAnnotationController; import seng302.controllers.annotations.ImportantAnnotationDelegate; import seng302.controllers.annotations.ImportantAnnotationsState; import seng302.models.*; +import seng302.models.mark.GateMark; +import seng302.models.mark.Mark; +import seng302.models.mark.MarkGroup; import seng302.models.stream.StreamParser; import java.io.IOException; @@ -185,14 +188,29 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel for (Yacht yacht : StreamParser.getBoatsPos().values()) { - if (yacht.getLastMarkRounded() != null) { - System.out.println("\n\nboat: " + yacht.getBoatName()); - System.out.println("last Mark: " + yacht.getLastMarkRounded().getName()); - } if (yacht.getNextMark() != null){ System.out.println("next Mark: " + yacht.getNextMark().getName()); for (BoatGroup bg : includedCanvasController.getBoatGroups()) { - bg.calculateLegDirection(); + + Boolean isUpwindLeg = null; + // Can only calc leg direction if there is a next mark and it is a gate mark + Mark nextMark = bg.getBoat().getNextMark(); + if (!(nextMark == null || !(nextMark instanceof GateMark))) { + isUpwindLeg = bg.isUpwindLeg(includedCanvasController); + } + + for (MarkGroup mg : includedCanvasController.getMarkGroups()) { + if (mg.getMainMark().equals(nextMark)) { + + } + } + if (isUpwindLeg != null) { + if (isUpwindLeg) { + + } + } + + } } diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java index 6ee1a3d8..41d02d08 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -12,7 +12,11 @@ import javafx.scene.shape.Line; import javafx.scene.shape.Polygon; import javafx.scene.text.Text; 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 java.text.DateFormat; @@ -364,33 +368,44 @@ public class BoatGroup extends Group { } - public void calculateLegDirection() { - Mark lastMark = boat.getLastMarkRounded(); + /** + * 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 = boat.getNextMark(); - if (lastMark == null || nextMark == null) { - return; + + 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. + */ + System.out.println("Boat Line func result: " + boatLineFuncResult); + System.out.println("Wind Line func result: " + windLineFuncResult); + if (boatLineFuncResult == windLineFuncResult) { + return true; + } else { + return false; } - Double windDirection = StreamParser.getWindDirection(); - Double arbitraryDistance = 10d; - - Point2D lastMarkMidPoint = new Point2D(lastMark.getLatitude(), lastMark.getLongitude()); - Point2D nextMarkMidPoint = new Point2D(nextMark.getLatitude(), nextMark.getLongitude()); - - Double windDirX = lastMarkMidPoint.getX() + (lastMarkMidPoint.getX() + arbitraryDistance -lastMarkMidPoint.getX())*Math.cos(windDirection) - (lastMarkMidPoint.getY() + arbitraryDistance -lastMarkMidPoint.getY())*Math.sin(windDirection); - Double windDirY = lastMarkMidPoint.getY() + (lastMarkMidPoint.getX() + arbitraryDistance -lastMarkMidPoint.getX())*Math.sin(windDirection) + (lastMarkMidPoint.getY() + arbitraryDistance -lastMarkMidPoint.getY())*Math.cos(windDirection); - Point2D windDirPoint = new Point2D(windDirX, windDirY); - - Double angle = lastMarkMidPoint.angle(nextMarkMidPoint, windDirPoint); - - if (angle <= 90) { - System.out.println(lastMark.getName() + " is downwind"); - System.out.println(nextMark.getName() + " is upwind"); - } - -// if (lastMarkMidPoint.angle(nextMarkMidPoint, windDirPoint) <= 90) { -// boat.getNextMark().s -// } } diff --git a/src/main/java/seng302/models/mark/MarkGroup.java b/src/main/java/seng302/models/mark/MarkGroup.java index a6651434..8c4e716b 100644 --- a/src/main/java/seng302/models/mark/MarkGroup.java +++ b/src/main/java/seng302/models/mark/MarkGroup.java @@ -161,4 +161,8 @@ public class MarkGroup extends Group { idArray[i++] = mark.getId(); return idArray; } + + public Mark getMainMark() { + return mainMark; + } } \ No newline at end of file diff --git a/src/test/java/seng302/TestGeoUtils.java b/src/test/java/seng302/TestGeoUtils.java new file mode 100644 index 00000000..633cec99 --- /dev/null +++ b/src/test/java/seng302/TestGeoUtils.java @@ -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); + } +} From 3cbbdb070f54932bbb5451bb8dfb1acd507ca59b Mon Sep 17 00:00:00 2001 From: William Muir Date: Wed, 24 May 2017 20:31:07 +1200 Subject: [PATCH 07/16] Basic implementation for mapping windspeed to draw a polar on a gate complete Created functionality to grab the closest windspeed value to map to VMG values based off the current wind speed in the Polar Table Created new RaceXML mark object which contains ALL marks for purposes of sequencing Displaying correct (?) polars for one point only on a gate Created functionality to receive leg data for each boat and then map that to the next gate. This may only work for the current race due to a slight fudge factor Created functionality to receive wind speed tags: #story[956] #pair[wmu16, mra106] --- pom.xml | 6 - src/main/java/seng302/App.java | 4 +- .../seng302/controllers/CanvasController.java | 2 +- .../controllers/RaceViewController.java | 119 +++++++++++++----- src/main/java/seng302/models/BoatGroup.java | 5 +- src/main/java/seng302/models/PolarTable.java | 47 +++++++ .../java/seng302/models/mark/MarkGroup.java | 6 +- .../seng302/models/stream/StreamParser.java | 38 +++--- .../java/seng302/models/stream/XMLParser.java | 50 +++++--- 9 files changed, 192 insertions(+), 85 deletions(-) diff --git a/pom.xml b/pom.xml index ea43245e..8d10c6fc 100644 --- a/pom.xml +++ b/pom.xml @@ -25,12 +25,6 @@ commons-io 1.3.2 - - - com.opencsv - opencsv - 3.9 - com.googlecode.json-simple json-simple diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index c53a4300..8806308b 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -56,7 +56,7 @@ public class App extends Application { sr = new StreamReceiver("localhost", 4949, "RaceStream"); break; case "staffserver": - sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream"); + sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4942, "RaceStream"); break; case "official": sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream"); @@ -67,7 +67,7 @@ public class App extends Application { else { // sr = new StreamReceiver("localhost", 4949, "RaceStream"); sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream"); -// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream"); +// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4942, "RaceStream"); } sr.start(); diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 36d56507..6e37b682 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -264,7 +264,7 @@ public class CanvasController { } private void initializeMarks() { - ArrayList allMarks = StreamParser.getXmlObject().getRaceXML().getCompoundMarks(); + List allMarks = StreamParser.getXmlObject().getRaceXML().getNonDupCompoundMarks(); for (Mark mark : allMarks) { if (mark.getMarkType() == MarkType.SINGLE_MARK) { SingleMark sMark = (SingleMark) mark; diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index 754da2eb..16e05352 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -6,6 +6,7 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; +import javafx.geometry.Point2D; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; @@ -29,7 +30,9 @@ 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.XMLParser; import java.io.IOException; import java.util.*; @@ -185,37 +188,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel updateWindDirection(); updateOrder(); updateBoatSelectionComboBox(); - - for (Yacht yacht : StreamParser.getBoatsPos().values()) { - - if (yacht.getNextMark() != null){ - System.out.println("next Mark: " + yacht.getNextMark().getName()); - for (BoatGroup bg : includedCanvasController.getBoatGroups()) { - - Boolean isUpwindLeg = null; - // Can only calc leg direction if there is a next mark and it is a gate mark - Mark nextMark = bg.getBoat().getNextMark(); - if (!(nextMark == null || !(nextMark instanceof GateMark))) { - isUpwindLeg = bg.isUpwindLeg(includedCanvasController); - } - - for (MarkGroup mg : includedCanvasController.getMarkGroups()) { - if (mg.getMainMark().equals(nextMark)) { - - } - } - if (isUpwindLeg != null) { - if (isUpwindLeg) { - - } - } - - - } - - } - } - + updateLaylines(); }) ); @@ -224,6 +197,43 @@ 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(); + + System.out.println("Leg Number: " + legNumber); + List markSequence = StreamParser.getXmlObject().getRaceXML().getCompoundMarkSequence(); + + if (legNumber == 0) { + System.out.println("PreStart"); + return null; + } else if (legNumber == markSequence.size() - 2) { + System.out.println("Finishing"); + 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 */ @@ -285,6 +295,53 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } + private void updateLaylines() { + + for (BoatGroup bg : includedCanvasController.getBoatGroups()) { + System.out.println("========" + bg.getBoat().getBoatName() + "========="); + 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) { + System.out.println("Next Mark: " + nextMark.getName()); + if (nextMark instanceof GateMark) { + if (bg.isUpwindLeg(includedCanvasController, nextMark)) { + isUpwind = true; + System.out.println(bg.getBoat().getBoatName() + " is on an upwind leg"); + } else { + isUpwind = false; + System.out.println(bg.getBoat().getBoatName() + " is on a downwind leg"); + } + + for(MarkGroup mg : includedCanvasController.getMarkGroups()) { + 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 angleAndSpeed; + if (isUpwind) { + angleAndSpeed = PolarTable.getOptimalUpwindVMG(StreamParser.getWindSpeed()); + } else { + angleAndSpeed = PolarTable.getOptimalDownwindVMG(StreamParser.getWindSpeed()); + } + + Double resultingAngle = angleAndSpeed.keySet().iterator().next(); + + mg.addLayLine(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection()); + mg.addLayLine(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection()); + + } + } + } + } + + System.out.println(); + } + } + + /** * Initialised the combo box with any boats currently in the race and adds the required listener * for the combobox to take action upon selection diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java index 41d02d08..32df62fa 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -375,8 +375,7 @@ public class BoatGroup extends Group { * 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 = boat.getNextMark(); + public Boolean isUpwindLeg(CanvasController canvasController, Mark nextMark) { Double windAngle = StreamParser.getWindDirection(); GateMark thisGateMark = (GateMark) nextMark; @@ -398,8 +397,6 @@ public class BoatGroup extends Group { boat is travelling into the wind. thus upwind. Otherwise if they are on different sides, then the boat is going with the wind. */ - System.out.println("Boat Line func result: " + boatLineFuncResult); - System.out.println("Wind Line func result: " + windLineFuncResult); if (boatLineFuncResult == windLineFuncResult) { return true; } else { diff --git a/src/main/java/seng302/models/PolarTable.java b/src/main/java/seng302/models/PolarTable.java index 578a3a7b..168d2291 100644 --- a/src/main/java/seng302/models/PolarTable.java +++ b/src/main/java/seng302/models/PolarTable.java @@ -1,6 +1,7 @@ package seng302.models; import java.io.*; +import java.util.ArrayList; import java.util.HashMap; /** @@ -113,4 +114,50 @@ public final class PolarTable { 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 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 getOptimalDownwindVMG(Double thisWindSpeed) { + + Double polarWindSpeed = getClosestMatch(thisWindSpeed); + return downwindOptimal.get(polarWindSpeed); + } + + + private static Double getClosestMatch(Double thisWindSpeed) { + + ArrayList 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; + } + } \ No newline at end of file diff --git a/src/main/java/seng302/models/mark/MarkGroup.java b/src/main/java/seng302/models/mark/MarkGroup.java index 8c4e716b..9e04399b 100644 --- a/src/main/java/seng302/models/mark/MarkGroup.java +++ b/src/main/java/seng302/models/mark/MarkGroup.java @@ -58,7 +58,7 @@ public class MarkGroup extends Group { * @param layLineAngle The angle the laylines point * @param baseAngle The reference angle */ - private void addLayLine(Point2D startPoint, Double layLineAngle, Double baseAngle){ + public void addLayLine(Point2D startPoint, Double layLineAngle, Double baseAngle){ Point2D ep1 = getPointRotation(startPoint, 50.0, baseAngle + -layLineAngle); Point2D ep2 = getPointRotation(startPoint, 50.0, baseAngle + layLineAngle); @@ -117,8 +117,8 @@ public class MarkGroup extends Group { //Laylines // if (mark.) - addLayLine(points1, 12.0, 90.0); - addLayLine(points2, 12.0, 90.0); +// addLayLine(points1, 12.0, 90.0); +// addLayLine(points2, 12.0, 90.0); } public void moveMarkTo (double x, double y, long raceId) diff --git a/src/main/java/seng302/models/stream/StreamParser.java b/src/main/java/seng302/models/stream/StreamParser.java index 00bfed25..dd82c250 100644 --- a/src/main/java/seng302/models/stream/StreamParser.java +++ b/src/main/java/seng302/models/stream/StreamParser.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; -import java.util.List; import java.util.Map; import java.util.TimeZone; import java.util.TreeMap; @@ -24,7 +23,6 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; import seng302.models.Yacht; import seng302.models.mark.Mark; -import seng302.models.stream.XMLParser.RaceXMLObject.Corner; import seng302.models.stream.packets.BoatPositionPacket; import seng302.models.stream.packets.StreamPacket; @@ -49,10 +47,15 @@ public class StreamParser extends Thread{ private static Map boats = new ConcurrentHashMap<>(); private static Map boatsPos = new ConcurrentSkipListMap<>(); private static double windDirection = 0; + private static Double windSpeed = 0d; private static Long currentTimeLong; private static String currentTimeString; 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 * @@ -196,7 +199,7 @@ public class StreamParser extends Thread{ int raceStatus = payload[11]; long expectedStartTime = bytesToLong(Arrays.copyOfRange(payload,12,18)); 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; DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); @@ -224,6 +227,7 @@ public class StreamParser extends Thread{ double windDirFactor = 0x4000 / 90; //0x4000 is 90 degrees, 0x8000 is 180 degrees, etc... windDirection = windDir / windDirFactor; + windSpeed = rawWindSpeed / 1000 * MS_TO_KNOTS; int noBoats = payload[22]; int raceType = payload[23]; @@ -445,25 +449,10 @@ public class StreamParser extends Thread{ // assign mark rounding time to boat boats.get((int)subjectId).setMarkRoundingTime(timeStamp); - for (Mark mark : xmlObject.getRaceXML().getCompoundMarks()) { + for (Mark mark : xmlObject.getRaceXML().getAllCompoundMarks()) { if (mark.getCompoundMarkID() == markId) { boats.get((int)subjectId).setLastMarkRounded(mark); - - List markSequence = xmlObject.getRaceXML().getCompoundMarkSequence(); - - for (int i = 0; i < markSequence.size() - 1; i++){ - Corner corner = markSequence.get(i); - - if (corner.getCompoundMarkID().equals(mark.getCompoundMarkID()) && (i + 1) < markSequence.size()){ - Corner nextCorner = markSequence.get(i+1); - for (Mark m : xmlObject.getRaceXML().getCompoundMarks()){ - if (m.getCompoundMarkID() == nextCorner.getCompoundMarkID()){ - boats.get((int)subjectId).setNextMark(m); - } - } - } - } - } + } } } @@ -593,6 +582,15 @@ public class StreamParser extends Thread{ 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 * diff --git a/src/main/java/seng302/models/stream/XMLParser.java b/src/main/java/seng302/models/stream/XMLParser.java index 4bda96cf..99ce72c8 100644 --- a/src/main/java/seng302/models/stream/XMLParser.java +++ b/src/main/java/seng302/models/stream/XMLParser.java @@ -235,7 +235,8 @@ public class XMLParser { //Non atomic race attributes private ArrayList participants; - private ArrayList course; + private ArrayList allMarks; + private ArrayList nonDuplicateMarks; private ArrayList compoundMarkSequence; private ArrayList courseLimit; @@ -283,7 +284,9 @@ public class XMLParser { } //Course - course = createCompoundMarks(docEle); + allMarks = new ArrayList<>(); + nonDuplicateMarks = new ArrayList<>(); + createCompoundMarks(docEle); //Course Mark Sequence compoundMarkSequence = new ArrayList<>(); @@ -312,26 +315,21 @@ public class XMLParser { } - private ArrayList createCompoundMarks(Element docEle) { - ArrayList cMarks = new ArrayList<>(); + private void createCompoundMarks(Element docEle) { NodeList cMarkList = docEle.getElementsByTagName("Course").item(0).getChildNodes(); for (int i = 0; i < cMarkList.getLength(); i++) { Node cMarkNode = cMarkList.item(i); if (cMarkNode.getNodeName().equals("CompoundMark")) { - Mark mark = createMark(cMarkNode); - if (mark != null) { - cMarks.add(mark); - } + createAndAddMark(cMarkNode); } } - - return cMarks; } - private Mark createMark(Node compoundMark) { + private void createAndAddMark(Node compoundMark) { + Boolean markSeen = false; List marksList = new ArrayList<>(); Integer compoundMarkID = getNodeAttributeInt(compoundMark, "CompoundMarkID"); String cMarkName = getNodeAttributeString(compoundMark, "Name"); @@ -354,20 +352,26 @@ public class XMLParser { for (SingleMark mark : marksList) { if (seenSourceIDs.contains(mark.getId())) { - return null; + markSeen = true; } else { seenSourceIDs.add(mark.getId()); } } + 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) { - 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(0).getLongitude(), compoundMarkID); - } else { - return null; + if(!markSeen) { + nonDuplicateMarks.add(thisGateMark); + } + allMarks.add(thisGateMark); } } @@ -396,8 +400,18 @@ public class XMLParser { return participants; } - public ArrayList getCompoundMarks() { - return course; + /** + * @return Returns ALL compound marks as stated in the RaceXML (INCLUDING DUPLICATE MARKS) + */ + public List getAllCompoundMarks() { + return allMarks; + } + + /** + * @return Returns Marks from the race XML without any duplicates + */ + public List getNonDupCompoundMarks() { + return nonDuplicateMarks; } public ArrayList getCompoundMarkSequence() { From ffe70a8313974c895b8b868748b06e14d9dde816 Mon Sep 17 00:00:00 2001 From: William Muir Date: Thu, 25 May 2017 00:22:15 +1200 Subject: [PATCH 08/16] Trying to get laylines to display on proper respective sides of the gate Having a great time here, Not quite working. There was an issue where the laylines are crossed and I think its tied to the fact that sometimes the two seperate points of the gate mark appear to have the same x y coordinate so the line function doesnt work? idk tags: #story[956] #pair[wmu16] --- .../controllers/RaceViewController.java | 16 +++++++-- .../java/seng302/models/mark/MarkGroup.java | 33 ++++++++++--------- .../seng302/models/stream/StreamParser.java | 2 ++ 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index 16e05352..eaba8dcc 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -22,6 +22,7 @@ import javafx.stage.Stage; import javafx.stage.StageStyle; import javafx.util.Duration; import javafx.util.StringConverter; +import seng302.GeometryUtils; import seng302.controllers.annotations.Annotation; import seng302.controllers.annotations.ImportantAnnotationController; import seng302.controllers.annotations.ImportantAnnotationDelegate; @@ -213,7 +214,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel if (legNumber == 0) { System.out.println("PreStart"); return null; - } else if (legNumber == markSequence.size() - 2) { + } else if (legNumber == markSequence.size() - 1) { System.out.println("Finishing"); return null; } @@ -329,8 +330,17 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel Double resultingAngle = angleAndSpeed.keySet().iterator().next(); - mg.addLayLine(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection()); - mg.addLayLine(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection()); + + Point2D boatCurrentPos = new Point2D(bg.getLayoutX(), bg.getLayoutY()); + Point2D gateMidPoint = markPoint1.midpoint(markPoint2); + Integer lineFuncResult = GeometryUtils.lineFunction(boatCurrentPos, gateMidPoint, markPoint2); + if (lineFuncResult == 1) { + mg.addRightLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection()); + mg.addLeftLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection()); + } else if (lineFuncResult == -1) { + mg.addRightLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection()); + mg.addLeftLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection()); + } } } diff --git a/src/main/java/seng302/models/mark/MarkGroup.java b/src/main/java/seng302/models/mark/MarkGroup.java index 9e04399b..553197a1 100644 --- a/src/main/java/seng302/models/mark/MarkGroup.java +++ b/src/main/java/seng302/models/mark/MarkGroup.java @@ -7,6 +7,7 @@ import javafx.scene.Group; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.shape.Line; +import seng302.GeometryUtils; /** * Created by CJIRWIN on 26/04/2017. @@ -52,27 +53,27 @@ public class MarkGroup extends Group { return new Point2D(newX, newY); } - /** - * Adds a lay-line to the MarkGroup - * @param startPoint The mark where the lay line starts - * @param layLineAngle The angle the laylines point - * @param baseAngle The reference angle - */ - public void addLayLine(Point2D startPoint, Double layLineAngle, Double baseAngle){ - Point2D ep1 = getPointRotation(startPoint, 50.0, baseAngle + -layLineAngle); - Point2D ep2 = getPointRotation(startPoint, 50.0, baseAngle + layLineAngle); + public void addLeftLayline(Point2D startPoint, Double layLineAngle, Double baseAngle) { - Line line1 = new Line(startPoint.getX(), startPoint.getY(), ep1.getX(), ep1.getY()); - Line line2 = new Line(startPoint.getX(), startPoint.getY(), ep2.getX(), ep2.getY()); + Point2D ep = getPointRotation(startPoint, 50.0, baseAngle + layLineAngle); + Line line = new Line(startPoint.getX(), startPoint.getY(), ep.getX(), ep.getY()); + line.setStrokeWidth(1); + line.setStroke(Color.GREEN); - line1.setStrokeWidth(0.5); - line1.setStroke(Color.GREEN); + super.getChildren().addAll(line); - line2.setStrokeWidth(0.5); - line2.setStroke(Color.GREEN); + } + + public void addRightLayline(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()); + line.setStrokeWidth(1); + line.setStroke(Color.GREEN); + + super.getChildren().addAll(line); - super.getChildren().addAll(line1, line2); } public MarkGroup(GateMark mark, Point2D points1, Point2D points2) { diff --git a/src/main/java/seng302/models/stream/StreamParser.java b/src/main/java/seng302/models/stream/StreamParser.java index dd82c250..88e99b8f 100644 --- a/src/main/java/seng302/models/stream/StreamParser.java +++ b/src/main/java/seng302/models/stream/StreamParser.java @@ -427,6 +427,8 @@ public class StreamParser extends Thread{ })); } markPositions.get(boatId).put(markPacket); + + } } From 68c3e3e999294cd566144aa7a0c4e0708617e1f9 Mon Sep 17 00:00:00 2001 From: William Muir Date: Thu, 25 May 2017 09:21:47 +1200 Subject: [PATCH 09/16] Merged develop onto this branchk tags: #story[956] --- src/main/java/seng302/controllers/CanvasController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index c18b145b..e39c0ebe 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -509,8 +509,8 @@ public class CanvasController { Point2D p1, p2; Mark m1, m2; double theta, distance, dx, dy, dHorizontal, dVertical; - m1 = new SingleMark("m1", maxLatPoint.getLatitude(), minLonPoint.getLongitude(), 1); - m2 = new SingleMark("m2", minLatPoint.getLatitude(), maxLonPoint.getLongitude(), 2); + m1 = new SingleMark("m1", maxLatPoint.getLatitude(), minLonPoint.getLongitude(), 1, 0); + m2 = new SingleMark("m2", minLatPoint.getLatitude(), maxLonPoint.getLongitude(), 2, 0); p1 = findScaledXY(m1); p2 = findScaledXY(m2); theta = Mark.calculateHeadingRad(m1, m2); From 99588c7ff87705d75dd98734d223ca9e57db5935 Mon Sep 17 00:00:00 2001 From: William Muir Date: Thu, 25 May 2017 09:54:00 +1200 Subject: [PATCH 10/16] Laylines now working upon selecting a boat. Still need to make their visibility togglable Currently just paints upon clicking tags: #story[956] --- .../seng302/controllers/RaceController.java | 0 .../controllers/RaceViewController.java | 90 +++++++++---------- src/main/java/seng302/models/BoatGroup.java | 23 ++++- 3 files changed, 66 insertions(+), 47 deletions(-) delete mode 100644 src/main/java/seng302/controllers/RaceController.java diff --git a/src/main/java/seng302/controllers/RaceController.java b/src/main/java/seng302/controllers/RaceController.java deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index ff5d2f6b..8e7d730b 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -282,7 +282,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel updateWindDirection(); updateOrder(); updateBoatSelectionComboBox(); - updateLaylines(); }) ); @@ -410,59 +409,57 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } - private void updateLaylines() { + private void updateLaylines(BoatGroup bg) { - for (BoatGroup bg : includedCanvasController.getBoatGroups()) { - System.out.println("========" + bg.getBoat().getBoatName() + "========="); - 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) { - System.out.println("Next Mark: " + nextMark.getName()); - if (nextMark instanceof GateMark) { - if (bg.isUpwindLeg(includedCanvasController, nextMark)) { - isUpwind = true; - System.out.println(bg.getBoat().getBoatName() + " is on an upwind leg"); - } else { - isUpwind = false; - System.out.println(bg.getBoat().getBoatName() + " is on a downwind leg"); - } + System.out.println("========" + bg.getBoat().getBoatName() + "========="); + 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) { + System.out.println("Next Mark: " + nextMark.getName()); + if (nextMark instanceof GateMark) { + if (bg.isUpwindLeg(includedCanvasController, nextMark)) { + isUpwind = true; + System.out.println(bg.getBoat().getBoatName() + " is on an upwind leg"); + } else { + isUpwind = false; + System.out.println(bg.getBoat().getBoatName() + " is on a downwind leg"); + } - for(MarkGroup mg : includedCanvasController.getMarkGroups()) { - 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 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.getLayoutX(), bg.getLayoutY()); - Point2D gateMidPoint = markPoint1.midpoint(markPoint2); - Integer lineFuncResult = GeometryUtils.lineFunction(boatCurrentPos, gateMidPoint, markPoint2); - if (lineFuncResult == 1) { - mg.addRightLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection()); - mg.addLeftLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection()); - } else if (lineFuncResult == -1) { - mg.addRightLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection()); - mg.addLeftLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection()); - } + for(MarkGroup mg : includedCanvasController.getMarkGroups()) { + 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 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); + if (lineFuncResult == 1) { + mg.addRightLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection()); + mg.addLeftLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection()); + } else if (lineFuncResult == -1) { + mg.addRightLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection()); + mg.addLeftLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection()); + } + } } } - - System.out.println(); } + + System.out.println(); } @@ -628,6 +625,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel if (bg.getBoat().getHullID().equals(yacht.getHullID())) { bg.setIsSelected(true); selectedBoat = yacht; + updateLaylines(bg); } else { bg.setIsSelected(false); } diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java index 32df62fa..d515f81b 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -59,6 +59,8 @@ public class BoatGroup extends Group { private Text estTimeToNextMarkObject; private Text legTimeObject; private Wake wake; + private Line leftLayLine; + private Line rightLayline; private Double distanceTravelled = 0.0; private Point2D lastPoint; private boolean destinationSet; @@ -158,10 +160,13 @@ public class BoatGroup extends Group { } + leftLayLine = new Line(); + rightLayline = new Line(); + wake = new Wake(0, -BOAT_HEIGHT); super.getChildren() .addAll(teamNameObject, velocityObject, boatPoly, estTimeToNextMarkObject, - legTimeObject); + legTimeObject, leftLayLine, rightLayline); } /** @@ -414,6 +419,7 @@ public class BoatGroup extends Group { setWakeVisible(isSelected); setEstTimeToNextMarkObjectVisible(isSelected); setLegTimeObjectVisible(isSelected); + setLayLinesVisible(isSelected); } @@ -441,6 +447,11 @@ public class BoatGroup extends Group { wake.setVisible(visible); } + public void setLayLinesVisible(Boolean visible) { + leftLayLine.setVisible(visible); + rightLayline.setVisible(visible); + } + public Yacht getBoat() { return boat; } @@ -467,6 +478,16 @@ public class BoatGroup extends Group { return group; } + + public Double getBoatLayoutX() { + return boatPoly.getLayoutX(); + } + + + public Double getBoatLayoutY() { + return boatPoly.getLayoutY(); + } + public boolean isStopped() { return isStopped; } From e72ac1def8738b5a0aa6e4f7b14526d409a8b001 Mon Sep 17 00:00:00 2001 From: Michael Rausch Date: Thu, 25 May 2017 13:48:48 +1200 Subject: [PATCH 11/16] Laylines are now togglable. Still not completely fool proof Laylines appear only for the selected boat. The dissapear if the boat is not seleccted. Laylines are the colour of the boat Laylines do not yet automatically update for a selected boat when it passes a mark There is still a polling interval of a second in which if you select a boat after it has just passed a mark it still thinks the next mark is that mark and laylines are displayed incorrectly #story[956] #pair[wmu16, mra106] --- .../controllers/RaceViewController.java | 54 +++++++++++++++++-- src/main/java/seng302/models/BoatGroup.java | 13 +++++ .../java/seng302/models/mark/MarkGroup.java | 40 ++++++-------- 3 files changed, 79 insertions(+), 28 deletions(-) diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index 8e7d730b..45041553 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -22,6 +22,7 @@ import javafx.scene.layout.Pane; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.paint.Paint; +import javafx.scene.shape.Line; import javafx.scene.text.Text; import javafx.stage.Stage; import javafx.stage.StageStyle; @@ -259,6 +260,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel color = yacht.getColour(); } } + if (color == null){ + return String.format( "#%02X%02X%02X",255,255,255); + } return String.format( "#%02X%02X%02X", (int)( color.getRed() * 255 ), (int)( color.getGreen() * 255 ), @@ -427,6 +431,9 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } for(MarkGroup mg : includedCanvasController.getMarkGroups()) { + + mg.removeLaylines(); + if (mg.getMainMark().getId() == nextMark.getId()) { SingleMark singleMark1 = ((GateMark) nextMark).getSingleMark1(); @@ -446,14 +453,25 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel 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) { - mg.addRightLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection()); - mg.addLeftLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection()); + rightLayline = makeRightLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection()); + leftLayline = makeLeftLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection()); } else if (lineFuncResult == -1) { - mg.addRightLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection()); - mg.addLeftLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection()); + 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); + } } } @@ -463,6 +481,32 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } + 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 * for the combobox to take action upon selection @@ -623,9 +667,9 @@ 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 //are to toggle its annotations, there is no other backwards knowledge of a yacht to its boatgroup. if (bg.getBoat().getHullID().equals(yacht.getHullID())) { + updateLaylines(bg); bg.setIsSelected(true); selectedBoat = yacht; - updateLaylines(bg); } else { bg.setIsSelected(false); } diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java index d515f81b..6784550b 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -1,5 +1,6 @@ package seng302.models; +import java.util.ArrayList; import javafx.event.EventHandler; import javafx.geometry.Point2D; import javafx.scene.CacheHint; @@ -452,6 +453,18 @@ public class BoatGroup extends Group { rightLayline.setVisible(visible); } + public void setLaylines(Line line1, Line line2) { + this.leftLayLine = line1; + this.rightLayline = line2; + } + + public ArrayList getLaylines() { + ArrayList laylines = new ArrayList<>(); + laylines.add(leftLayLine); + laylines.add(rightLayline); + return laylines; + } + public Yacht getBoat() { return boat; } diff --git a/src/main/java/seng302/models/mark/MarkGroup.java b/src/main/java/seng302/models/mark/MarkGroup.java index 553197a1..011154a6 100644 --- a/src/main/java/seng302/models/mark/MarkGroup.java +++ b/src/main/java/seng302/models/mark/MarkGroup.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import javafx.geometry.Point2D; import javafx.scene.Group; +import javafx.scene.Node; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.shape.Line; @@ -46,34 +47,27 @@ public class MarkGroup extends Group { super.getChildren().add(markCircle); } - 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); + public void addLaylines(Line line1, Line line2) { - return new Point2D(newX, newY); + super.getChildren().addAll(line1, line2); } - public void addLeftLayline(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()); - line.setStrokeWidth(1); - line.setStroke(Color.GREEN); - - super.getChildren().addAll(line); - - } - - public void addRightLayline(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()); - line.setStrokeWidth(1); - line.setStroke(Color.GREEN); - - super.getChildren().addAll(line); + public void removeLaylines() { + ArrayList 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) { From 53f6a6b8c54afdfbaf1a6e6b8d545f5d8489bf5c Mon Sep 17 00:00:00 2001 From: Kusal Ekanayake Date: Thu, 25 May 2017 14:06:33 +1200 Subject: [PATCH 12/16] Implimented a new way to determine positions #story[952] --- src/main/java/seng302/App.java | 5 +- .../seng302/controllers/CanvasController.java | 8 +-- .../java/seng302/controllers/Controller.java | 2 +- .../controllers/RaceViewController.java | 3 +- .../controllers/StartScreenController.java | 2 +- src/main/java/seng302/models/Yacht.java | 1 + .../seng302/models/stream/StreamParser.java | 68 ++++++++++++------- src/main/resources/views/RaceView.fxml | 2 +- 8 files changed, 56 insertions(+), 35 deletions(-) diff --git a/src/main/java/seng302/App.java b/src/main/java/seng302/App.java index a3d5f49f..99bcb90b 100644 --- a/src/main/java/seng302/App.java +++ b/src/main/java/seng302/App.java @@ -63,9 +63,10 @@ public class App extends Application { } //Change the StreamReceiver in this else block to change the default data source. 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("livedata.americascup.com", 4941, "RaceStream"); +// sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4942, "RaceStream"); +// sr = new StreamReceiver("livedata.americascup.com", 4941, "RaceStream"); } sr.start(); diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 96e719e6..604822db 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -214,7 +214,7 @@ public class CanvasController { for (BoatGroup boatGroup : boatGroups) { // some raceObjects will have multiple ID's (for instance gate marks) //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()) { updateBoatGroup(boatGroup); } @@ -223,7 +223,7 @@ public class CanvasController { } for (MarkGroup markGroup : markGroups) { for (Long id : markGroup.getRaceIds()) { - if (StreamParser.markPositions.containsKey(id)) { + if (StreamParser.markLocations.containsKey(id)) { updateMarkGroup(id, markGroup); } } @@ -240,7 +240,7 @@ public class CanvasController { } private void updateBoatGroup(BoatGroup boatGroup) { - PriorityBlockingQueue movementQueue = StreamParser.boatPositions.get(boatGroup.getRaceId()); + PriorityBlockingQueue movementQueue = StreamParser.boatLocations.get(boatGroup.getRaceId()); // giving the movementQueue a 5 packet buffer to account for slightly out of order packets if (movementQueue.size() > 0){ try { @@ -256,7 +256,7 @@ public class CanvasController { } void updateMarkGroup (long raceId, MarkGroup markGroup) { - PriorityBlockingQueue movementQueue = StreamParser.markPositions.get(raceId); + PriorityBlockingQueue movementQueue = StreamParser.markLocations.get(raceId); if (movementQueue.size() > 0){ try { BoatPositionPacket positionPacket = movementQueue.take(); diff --git a/src/main/java/seng302/controllers/Controller.java b/src/main/java/seng302/controllers/Controller.java index a222cfc3..73b3766b 100644 --- a/src/main/java/seng302/controllers/Controller.java +++ b/src/main/java/seng302/controllers/Controller.java @@ -34,6 +34,6 @@ public class Controller implements Initializable { public void initialize(URL location, ResourceBundle resources) { contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString()); setContentPane("/views/StartScreenView.fxml"); - StreamParser.boatPositions.clear(); + StreamParser.boatLocations.clear(); } } diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index 03b51cfa..363d5fea 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -67,7 +67,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel @FXML private CanvasController includedCanvasController; - private ArrayList startingBoats = new ArrayList<>(); + private static ArrayList startingBoats = new ArrayList<>(); private boolean displayFps; private Timeline timerTimeline; private Stage stage; @@ -221,7 +221,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel } }); - // Adds the new data series to the sparkline (and set the colour of the series) raceSparkLine.setCreateSymbols(false); positions.stream().filter(spark -> !raceSparkLine.getData().contains(spark)).forEach(spark -> { diff --git a/src/main/java/seng302/controllers/StartScreenController.java b/src/main/java/seng302/controllers/StartScreenController.java index 5fe0036c..b2238826 100644 --- a/src/main/java/seng302/controllers/StartScreenController.java +++ b/src/main/java/seng302/controllers/StartScreenController.java @@ -138,7 +138,7 @@ public class StartScreenController implements Initializable { } public void switchToRaceView() { - StreamParser.boatPositions.clear(); + StreamParser.boatLocations.clear(); switchedToRaceView = true; setContentPane("/views/RaceView.fxml"); } diff --git a/src/main/java/seng302/models/Yacht.java b/src/main/java/seng302/models/Yacht.java index c5bdcbb7..1c2fca0b 100644 --- a/src/main/java/seng302/models/Yacht.java +++ b/src/main/java/seng302/models/Yacht.java @@ -6,6 +6,7 @@ import seng302.controllers.RaceViewController; import java.text.DateFormat; import java.text.SimpleDateFormat; +import seng302.models.stream.StreamParser; /** * Yacht class for the racing boat. diff --git a/src/main/java/seng302/models/stream/StreamParser.java b/src/main/java/seng302/models/stream/StreamParser.java index 15b45237..b1be7f18 100644 --- a/src/main/java/seng302/models/stream/StreamParser.java +++ b/src/main/java/seng302/models/stream/StreamParser.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; -import java.util.HashMap; import java.util.Map; import java.util.TimeZone; import java.util.TreeMap; @@ -34,8 +33,8 @@ import seng302.models.stream.packets.StreamPacket; */ public class StreamParser extends Thread { - public static ConcurrentHashMap> markPositions = new ConcurrentHashMap<>(); - public static ConcurrentHashMap> boatPositions = new ConcurrentHashMap<>(); + public static ConcurrentHashMap> markLocations = new ConcurrentHashMap<>(); + public static ConcurrentHashMap> boatLocations = new ConcurrentHashMap<>(); private String threadName; private Thread t; private static boolean newRaceXmlReceived = false; @@ -45,7 +44,7 @@ public class StreamParser extends Thread { private static boolean streamStatus = false; private static long timeSinceStart = -1; private static Map boats = new ConcurrentHashMap<>(); - private static Map boatsPos = new ConcurrentSkipListMap<>(); + private static Map boatsPos = new ConcurrentSkipListMap<>(); private static double windDirection = 0; private static Long currentTimeLong; private static String currentTimeString; @@ -145,6 +144,7 @@ public class StreamParser extends Thread { } } catch (NullPointerException e) { System.out.println("Error parsing packet"); + e.printStackTrace(); } } @@ -218,13 +218,12 @@ public class StreamParser extends Thread { int noBoats = payload[22]; int raceType = payload[23]; - boatsPos = new TreeMap<>(); for (int i = 0; i < noBoats; i++) { long boatStatusSourceID = bytesToLong( Arrays.copyOfRange(payload, 24 + (i * 20), 28 + (i * 20))); Yacht boat = boats.get((int) boatStatusSourceID); 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.setPenaltiesServed((int) payload[31 + (i * 20)]); Long estTimeAtNextMark = bytesToLong( @@ -233,6 +232,7 @@ public class StreamParser extends Thread { Long estTimeAtFinish = bytesToLong( Arrays.copyOfRange(payload, 38 + (i * 20), 44 + (i * 20))); boat.setEstimateTimeAtFinish(estTimeAtFinish); +// boatsPos.put(estTimeAtFinish, boat); // String boatStatus = "SourceID: " + boatStatusSourceID; // boatStatus += "\nBoat Status: " + (int)payload[28 + (i * 20)]; // boatStatus += "\nLegNumber: " + (int)payload[29 + (i * 20)]; @@ -242,16 +242,35 @@ public class StreamParser extends Thread { // boatStatus += "\nEstTimeAtFinish: " + bytesToLong(Arrays.copyOfRange(payload,37 + (i * 20),43+ (i * 20))); // boatStatuses.add(boatStatus); } - if (isRaceStarted()) { - int pos = 1; - for (Yacht yacht : boatsPos.values()) { - yacht.setPosition(String.valueOf(pos)); - pos++; - } - } else { - for (Yacht yacht : boatsPos.values()) { - yacht.setPosition("-"); +// if (isRaceStarted()) { +// int pos = 1; +// for (Yacht yacht : boatsPos.values()) { +// yacht.setPosition(String.valueOf(pos)); +// pos++; +// } +// } else { +// for (Yacht yacht : boatsPos.values()) { +// yacht.setPosition("-"); +// } +// } + } + + private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){ + Integer placing = 1; + if (leg != updatingBoat.getLegNumber() && (raceStarted || raceFinished)) { + for (Yacht boat : boats.values()) { + if (boat.getLegNumber() != null && leg <= boat.getLegNumber()){ + placing += 1; + } } + System.out.println("updatingBoat.getBoatName() + = " + updatingBoat.getBoatName() + " " + placing); + 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 +414,9 @@ public class StreamParser extends Thread { BoatPositionPacket boatPacket = new BoatPositionPacket(boatId, timeValid, lat, lon, heading, groundSpeed); - //add a new priority que to the boatPositions HashMap - if (!boatPositions.containsKey(boatId)) { - boatPositions.put(boatId, + //add a new priority que to the boatLocations HashMap + if (!boatLocations.containsKey(boatId)) { + boatLocations.put(boatId, new PriorityBlockingQueue<>(256, new Comparator() { @Override public int compare(BoatPositionPacket p1, BoatPositionPacket p2) { @@ -405,14 +424,14 @@ public class StreamParser extends Thread { } })); } - boatPositions.get(boatId).put(boatPacket); + boatLocations.get(boatId).put(boatPacket); } else if (deviceType == 3) { BoatPositionPacket markPacket = new BoatPositionPacket(boatId, timeValid, lat, lon, heading, groundSpeed); - //add a new priority que to the boatPositions HashMap - if (!markPositions.containsKey(boatId)) { - markPositions.put(boatId, + //add a new priority que to the boatLocations HashMap + if (!markLocations.containsKey(boatId)) { + markLocations.put(boatId, new PriorityBlockingQueue<>(256, new Comparator() { @Override public int compare(BoatPositionPacket p1, BoatPositionPacket p2) { @@ -420,7 +439,7 @@ public class StreamParser extends Thread { } })); } - markPositions.get(boatId).put(markPacket); + markLocations.get(boatId).put(markPacket); } } @@ -592,7 +611,8 @@ public class StreamParser extends Thread { * * @return a map of time to finish and boat. */ - public static Map getBoatsPos() { + public static Map getBoatsPos() { + return boatsPos; } diff --git a/src/main/resources/views/RaceView.fxml b/src/main/resources/views/RaceView.fxml index b225ec98..59741514 100644 --- a/src/main/resources/views/RaceView.fxml +++ b/src/main/resources/views/RaceView.fxml @@ -25,7 +25,7 @@