From f1ad03e91398cae1cd8cb5e8e5467001056af2b0 Mon Sep 17 00:00:00 2001 From: Calum Date: Mon, 31 Jul 2017 02:19:19 +1200 Subject: [PATCH] Refactored the setup for MarkObjects (now renamed Markers) and made the CompoundMark + Mark + GeoPoint classes the standard across all classes instead of GateMark + SingleMark + Mark. #refactor --- .../{utilities => model}/GeoPoint.java | 6 +- src/main/java/seng302/model/Limit.java | 15 +- src/main/java/seng302/model/Yacht.java | 1 - .../java/seng302/model/mark/CompoundMark.java | 86 ++++ .../java/seng302/model/{ => mark}/Corner.java | 2 +- .../java/seng302/model/mark/GateMark.java | 49 --- src/main/java/seng302/model/mark/Mark.java | 151 ++----- .../java/seng302/model/mark/MarkType.java | 9 - .../java/seng302/model/mark/SingleMark.java | 34 -- .../model/stream/xml/parser/RaceXMLData.java | 19 +- .../java/seng302/server/simulator/Boat.java | 3 +- .../server/simulator/{mark => }/Corner.java | 4 +- .../simulator/{mark => }/RoundingType.java | 2 +- .../seng302/server/simulator/Simulator.java | 5 +- .../server/simulator/mark/CompoundMark.java | 70 ---- .../seng302/server/simulator/mark/Mark.java | 55 --- .../simulator/parsers/CourseParser.java | 12 +- .../server/simulator/parsers/RaceParser.java | 2 +- .../java/seng302/utilities/GeoUtility.java | 24 +- .../java/seng302/utilities/XMLParser.java | 33 +- .../java/seng302/visualiser/GameView.java | 385 +++++++++--------- .../visualiser/fxObjects/BoatObject.java | 2 + .../visualiser/fxObjects/CourseBorder.java | 15 + .../seng302/visualiser/fxObjects/Gate.java | 20 + .../visualiser/fxObjects/MarkObject.java | 167 -------- .../seng302/visualiser/fxObjects/Marker.java | 19 + .../java/seng302/visualiser/map/Boundary.java | 4 +- .../seng302/visualiser/map/CanvasMap.java | 6 +- .../visualiser/map/MercatorProjection.java | 2 +- src/test/java/seng302/models/YachtTest.java | 2 +- .../server/simulator/GeoUtilityTest.java | 2 +- .../map/MercatorProjectionTest.java | 2 +- 32 files changed, 452 insertions(+), 756 deletions(-) rename src/main/java/seng302/{utilities => model}/GeoPoint.java (76%) create mode 100644 src/main/java/seng302/model/mark/CompoundMark.java rename src/main/java/seng302/model/{ => mark}/Corner.java (96%) delete mode 100644 src/main/java/seng302/model/mark/GateMark.java delete mode 100644 src/main/java/seng302/model/mark/MarkType.java delete mode 100644 src/main/java/seng302/model/mark/SingleMark.java rename src/main/java/seng302/server/simulator/{mark => }/Corner.java (96%) rename src/main/java/seng302/server/simulator/{mark => }/RoundingType.java (95%) delete mode 100644 src/main/java/seng302/server/simulator/mark/CompoundMark.java delete mode 100644 src/main/java/seng302/server/simulator/mark/Mark.java create mode 100644 src/main/java/seng302/visualiser/fxObjects/CourseBorder.java create mode 100644 src/main/java/seng302/visualiser/fxObjects/Gate.java delete mode 100644 src/main/java/seng302/visualiser/fxObjects/MarkObject.java create mode 100644 src/main/java/seng302/visualiser/fxObjects/Marker.java diff --git a/src/main/java/seng302/utilities/GeoPoint.java b/src/main/java/seng302/model/GeoPoint.java similarity index 76% rename from src/main/java/seng302/utilities/GeoPoint.java rename to src/main/java/seng302/model/GeoPoint.java index a3d5c54b..29938946 100644 --- a/src/main/java/seng302/utilities/GeoPoint.java +++ b/src/main/java/seng302/model/GeoPoint.java @@ -1,12 +1,12 @@ -package seng302.utilities; +package seng302.model; /** - * A class represent Geo location (latitude, longitude). + * A class represent Geo location (latitude, lnggitude). * Created by Haoming on 15/5/2017 */ public class GeoPoint { - double lat, lng; + private double lat, lng; public GeoPoint(double lat, double lng) { this.lat = lat; diff --git a/src/main/java/seng302/model/Limit.java b/src/main/java/seng302/model/Limit.java index 4c873c91..e93ed2fe 100644 --- a/src/main/java/seng302/model/Limit.java +++ b/src/main/java/seng302/model/Limit.java @@ -3,27 +3,16 @@ package seng302.model; /** * Stores data on the border of a race */ -public class Limit { +public class Limit extends GeoPoint { private Integer seqID; - private Double lat; - private Double lng; public Limit(Integer seqID, Double lat, Double lng) { + super(lat, lng); this.seqID = seqID; - this.lat = lat; - this.lng = lng; } public Integer getSeqID() { return seqID; } - - public Double getLat() { - return lat; - } - - public Double getLng() { - return lng; - } } \ No newline at end of file diff --git a/src/main/java/seng302/model/Yacht.java b/src/main/java/seng302/model/Yacht.java index 23873787..1d54696f 100644 --- a/src/main/java/seng302/model/Yacht.java +++ b/src/main/java/seng302/model/Yacht.java @@ -14,7 +14,6 @@ import javafx.beans.property.ReadOnlyLongWrapper; import javafx.scene.paint.Color; import seng302.gameServer.GameState; import seng302.model.mark.Mark; -import seng302.utilities.GeoPoint; /** * Yacht class for the racing boat. diff --git a/src/main/java/seng302/model/mark/CompoundMark.java b/src/main/java/seng302/model/mark/CompoundMark.java new file mode 100644 index 00000000..8355caa4 --- /dev/null +++ b/src/main/java/seng302/model/mark/CompoundMark.java @@ -0,0 +1,86 @@ +package seng302.model.mark; + +import com.sun.deploy.util.StringUtils; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CompoundMark { + + private int compoundMarkId; + private String name; + + private List marks = new ArrayList<>(); + + public CompoundMark(int markID, String name) { + this.compoundMarkId = markID; + this.name = name; + } + + public void addSubMarks(Mark... marks) { + this.marks.addAll(Arrays.asList(marks)); + } + + public void addSubMarks(List marks) { + this.marks.addAll(marks); + } + + /** + * Prints out compoundMark's info and its marks, good for testing + * @return a string showing its details + */ + @Override + public String toString(){ + String info = String.format("CompoundMark: %d (%s), [", compoundMarkId, name); + info += StringUtils.join(marks, ", ") + "]"; + return info; + } + + public int getId() { + return compoundMarkId; + } + + public void setId (int markID) { + this.compoundMarkId = markID; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + /** + * Returns the mark contained in the compound mark. Marks are numbered 1 to n; + * @param singleMarkId the id of the desired mark contained in this compound mark. + * @return the desired mark. Returns null if the ID is not in range (1, NUM_MARKS) + */ + public Mark getSubMark(int singleMarkId) { + try { + return marks.get(singleMarkId - 1); + } catch (IndexOutOfBoundsException e) { + return null; + } + } + + /** + * Returns whether or not this CompoundMark is a Gate. It is generally cleaner to program to a + * specific singleMark or the list of marks. + * + * @return True if the compound mark is a gate, false otherwise. + */ + public boolean isGate () { + return marks.size() > 1; + } + + /** + * Returns the list of marks in the compoundMark + * + * @return All marks contained in this mark. + */ + public List getMarks () { + return marks; + } +} diff --git a/src/main/java/seng302/model/Corner.java b/src/main/java/seng302/model/mark/Corner.java similarity index 96% rename from src/main/java/seng302/model/Corner.java rename to src/main/java/seng302/model/mark/Corner.java index 77d5eed4..c911ce9c 100644 --- a/src/main/java/seng302/model/Corner.java +++ b/src/main/java/seng302/model/mark/Corner.java @@ -1,4 +1,4 @@ -package seng302.model; +package seng302.model.mark; /** * Stores the data for the cornering of a mark. diff --git a/src/main/java/seng302/model/mark/GateMark.java b/src/main/java/seng302/model/mark/GateMark.java deleted file mode 100644 index 896fbd66..00000000 --- a/src/main/java/seng302/model/mark/GateMark.java +++ /dev/null @@ -1,49 +0,0 @@ -package seng302.model.mark; - -/** - * To represent a gate mark which contains two single marks. - * Created by ptg19 on 16/03/17. - * Modified by Haoming Yin (hyi25) on 17/3/2017. - */ -public class GateMark extends Mark { - - private SingleMark singleMark1; - private SingleMark singleMark2; - - /** - * Create an instance of Gate Mark which contains two single mark - * @param name the name of the gate 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, int compoundMarkID) { - super(name, type, latitude, longitude, compoundMarkID); - this.singleMark1 = singleMark1; - this.singleMark2 = singleMark2; - } - - public SingleMark getSingleMark1() { - return singleMark1; - } - - public void setSingleMark1(SingleMark singleMark1) { - this.singleMark1 = singleMark1; - } - - public SingleMark getSingleMark2() { - return singleMark2; - } - - public void setSingleMark2(SingleMark singleMark2) { - this.singleMark2 = singleMark2; - } - - public double getLatitude(){ - return (this.getSingleMark1().getLatitude()); - } - - public double getLongitude(){ - return (this.getSingleMark1().getLongitude()); - } - -} diff --git a/src/main/java/seng302/model/mark/Mark.java b/src/main/java/seng302/model/mark/Mark.java index 65c60195..57d04974 100644 --- a/src/main/java/seng302/model/mark/Mark.java +++ b/src/main/java/seng302/model/mark/Mark.java @@ -1,113 +1,46 @@ package seng302.model.mark; +import java.util.ArrayList; +import java.util.List; +import seng302.model.GeoPoint; + /** * An abstract class to represent general marks * Created by Haoming Yin (hyi25) on 17/3/17. */ -public abstract class Mark { +public class Mark extends GeoPoint { + @FunctionalInterface + public interface PositionListener { + void notifyPositionChange(Mark mark, double lat, double lon); + } + + private int seqID; private String name; - private MarkType markType; - private double latitude; - private double longitude; - private int id; - private int compoundMarkID; + private int sourceID; + private List positionListeners = new ArrayList<>(); - /** - * 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 sourceID, int compoundMarkID) { + public Mark(String name, double lat, double lng, int sourceID) { + super(lat, lng); this.name = name; - this.markType = markType; - this.id = sourceID; - this.compoundMarkID = compoundMarkID; - } - - public Mark(String name, MarkType markType, double latitude, double longitude, int compoundMarkID) { - this.name = name; - this.markType = markType; - this.latitude = latitude; - this.longitude = longitude; - this.id = 0; - this.compoundMarkID = compoundMarkID; + this.sourceID = sourceID; } /** - * Calculated the heading in radians from first Mark to the second Mark. - * - * @param pointOne First Mark - * @param pointTwo Second Mark - * @return Heading in radians + * Prints out mark's info and its geo location, good for testing + * @return a string showing its details */ - public static Double calculateHeadingRad(Mark pointOne, Mark pointTwo) { - Double longitude1 = pointOne.getLongitude(); - Double longitude2 = pointTwo.getLongitude(); - Double latitude1 = pointOne.getLatitude(); - Double latitude2 = pointTwo.getLatitude(); - return calculateHeadingRad(latitude1, longitude1, latitude2, longitude2); + @Override + public String toString() { + return String.format("Mark%d: %s, source: %d, lat: %f, lng: %f", seqID, name, sourceID, getLat(), getLng()); } - /** - * Calculate the heading in radians from geographical location with latitude1, longitude 1 to - * geographical latitude2, longitude 2 - * - * @param longitude1 Longitude of first point in degrees - * @param longitude2 Longitude of second point in degrees - * @param latitude1 Latitude of first point in degrees - * @param latitude2 Latitude of first point in degrees - * @return Heading in radians - */ - public static double calculateHeadingRad(Double latitude1, Double longitude1, Double latitude2, - Double longitude2) { - latitude1 = Math.toRadians(latitude1); - latitude2 = Math.toRadians(latitude2); - Double longDiff = Math.toRadians(longitude2 - longitude1); - Double y = Math.sin(longDiff) * Math.cos(latitude2); - Double x = - Math.cos(latitude1) * Math.sin(latitude2) - Math.sin(latitude1) * Math.cos(latitude2) - * Math.cos(longDiff); - return Math.atan2(y, x); + public int getSeqID() { + return seqID; } - /** - * Calculates the distance in meters from the first Mark to a second Mark - * - * @param pointOne First Mark - * @param pointTwo Second Mark - * @return Distance in meters - */ - public static Double calculateDistance(Mark pointOne, Mark pointTwo) { - Double longitude1 = pointOne.getLongitude(); - Double longitude2 = pointTwo.getLongitude(); - Double latitude1 = pointOne.getLatitude(); - Double latitude2 = pointTwo.getLatitude(); - return calculateDistance(latitude1, longitude1, latitude2, longitude2); - } - - /** - * Calculate the distance in meters from geographical location with latitude1, longitude 1 to - * geographical latitude2, longitude 2 - * - * @param longitude1 Longitude of first point in degrees - * @param longitude2 Longitude of second point in degrees - * @param latitude1 Latitude of first point in degrees - * @param latitude2 Latitude of first point in degrees - * @return Distance in meters - */ - public static Double calculateDistance(Double latitude1, Double longitude1, Double latitude2, - Double longitude2) { - Double theta = longitude1 - longitude2; - Double dist = Math.sin(Math.toRadians(latitude1)) * Math.sin(Math.toRadians(latitude2)) + - Math.cos(Math.toRadians(latitude1)) * Math.cos(Math.toRadians(latitude2)) * - Math.cos(Math.toRadians(theta)); - dist = Math.acos(dist); - dist = Math.toDegrees(dist); - dist = dist * 60 - * 1.1508; //nautical mile (distance between two degrees) * (degrees in a minute) - dist = dist * 1609.344; //ratio of miles to metres - return dist; + public void setSeqID(int seqID) { + this.seqID = seqID; } public String getName() { @@ -118,31 +51,29 @@ public abstract class Mark { this.name = name; } - public MarkType getMarkType() { - return markType; + public int getSourceID() { + return sourceID; } - public void setMarkType(MarkType markType) { - this.markType = markType; + public void setSourceID(int sourceID) { + this.sourceID = sourceID; } - public double getLatitude() { - return latitude; + public void updatePosition (double lat, double lon) { + this.setLat(lat); + this.setLng(lon); + for (PositionListener listener : positionListeners) { + listener.notifyPositionChange(this, lat, lon); + } } - public double getLongitude() { - return longitude; + public void addPositionListener (PositionListener listener) { + positionListeners.add(listener); } - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public int getCompoundMarkID() { - return compoundMarkID; + public void removePositionListener (PositionListener listener) { + positionListeners.remove(listener); } } + + diff --git a/src/main/java/seng302/model/mark/MarkType.java b/src/main/java/seng302/model/mark/MarkType.java deleted file mode 100644 index c1d6bc6d..00000000 --- a/src/main/java/seng302/model/mark/MarkType.java +++ /dev/null @@ -1,9 +0,0 @@ -package seng302.model.mark; - -/** - * To represent two types of mark - * Created by Haoming Yin (hyi25) on 17/3/17. - */ -public enum MarkType { - SINGLE_MARK, OPEN_GATE -} diff --git a/src/main/java/seng302/model/mark/SingleMark.java b/src/main/java/seng302/model/mark/SingleMark.java deleted file mode 100644 index f7c57dbb..00000000 --- a/src/main/java/seng302/model/mark/SingleMark.java +++ /dev/null @@ -1,34 +0,0 @@ -package seng302.model.mark; - -/** - * Represents the marker as a single mark - * - * Created by Haoming Yin (hyi25) on 17/3/2017 - */ -public class SingleMark extends Mark { - private double lat; - private double lon; - private String name; - - /** - * Represents a marker - * - * @param name, the name of the marker* - * @param lat, the latitude of the marker - * @param lon, the longitude of the marker - */ - 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; - } - - - public double getLatitude() { - return this.lat; - } - - public double getLongitude() { - return this.lon; - } -} \ No newline at end of file diff --git a/src/main/java/seng302/model/stream/xml/parser/RaceXMLData.java b/src/main/java/seng302/model/stream/xml/parser/RaceXMLData.java index 02ba0686..6a7e5ca9 100644 --- a/src/main/java/seng302/model/stream/xml/parser/RaceXMLData.java +++ b/src/main/java/seng302/model/stream/xml/parser/RaceXMLData.java @@ -3,9 +3,9 @@ package seng302.model.stream.xml.parser; import java.util.HashMap; import java.util.List; import java.util.Map; -import seng302.model.Corner; import seng302.model.Limit; -import seng302.model.mark.Mark; +import seng302.model.mark.CompoundMark; +import seng302.model.mark.Corner; /** * Process a Document object containing race data in XML format and stores the data. @@ -13,20 +13,18 @@ import seng302.model.mark.Mark; public class RaceXMLData { private List participants; - private Map compoundMarks; + private Map compoundMarks; private List markSequence; private List courseLimit; - private Map individualMarks; - public RaceXMLData(List participants, List compoundMarks, List markSequence, - List courseLimit) { + public RaceXMLData(List participants, List compoundMarks, + List markSequence, List courseLimit) { this.participants = participants; this.markSequence = markSequence; this.courseLimit = courseLimit; this.compoundMarks = new HashMap<>(); - for (Mark mark : compoundMarks) - this.compoundMarks.put(mark.getId(), mark); - for (Mark mark : compoundMarks) { + for (CompoundMark cMark : compoundMarks) { + this.compoundMarks.put(cMark.getId(), cMark); } } @@ -34,7 +32,7 @@ public class RaceXMLData { return participants; } - public Map getCompoundMarks() { + public Map getCompoundMarks() { return compoundMarks; } @@ -45,4 +43,5 @@ public class RaceXMLData { public List getCourseLimit() { return courseLimit; } + } diff --git a/src/main/java/seng302/server/simulator/Boat.java b/src/main/java/seng302/server/simulator/Boat.java index 435a70b6..1475a1e0 100644 --- a/src/main/java/seng302/server/simulator/Boat.java +++ b/src/main/java/seng302/server/simulator/Boat.java @@ -1,7 +1,6 @@ package seng302.server.simulator; -import seng302.server.simulator.mark.Corner; -import seng302.utilities.GeoPoint; +import seng302.model.GeoPoint; import seng302.utilities.GeoUtility; public class Boat { diff --git a/src/main/java/seng302/server/simulator/mark/Corner.java b/src/main/java/seng302/server/simulator/Corner.java similarity index 96% rename from src/main/java/seng302/server/simulator/mark/Corner.java rename to src/main/java/seng302/server/simulator/Corner.java index 136212f2..85a9d839 100644 --- a/src/main/java/seng302/server/simulator/mark/Corner.java +++ b/src/main/java/seng302/server/simulator/Corner.java @@ -1,4 +1,6 @@ -package seng302.server.simulator.mark; +package seng302.server.simulator; + +import seng302.model.mark.CompoundMark; public class Corner { diff --git a/src/main/java/seng302/server/simulator/mark/RoundingType.java b/src/main/java/seng302/server/simulator/RoundingType.java similarity index 95% rename from src/main/java/seng302/server/simulator/mark/RoundingType.java rename to src/main/java/seng302/server/simulator/RoundingType.java index de6f6133..d13ae01e 100644 --- a/src/main/java/seng302/server/simulator/mark/RoundingType.java +++ b/src/main/java/seng302/server/simulator/RoundingType.java @@ -1,4 +1,4 @@ -package seng302.server.simulator.mark; +package seng302.server.simulator; public enum RoundingType { diff --git a/src/main/java/seng302/server/simulator/Simulator.java b/src/main/java/seng302/server/simulator/Simulator.java index e0da9cf3..24075722 100644 --- a/src/main/java/seng302/server/simulator/Simulator.java +++ b/src/main/java/seng302/server/simulator/Simulator.java @@ -3,10 +3,9 @@ package seng302.server.simulator; import java.util.List; import java.util.Observable; import java.util.concurrent.ThreadLocalRandom; -import seng302.server.simulator.mark.Corner; -import seng302.server.simulator.mark.Mark; +import seng302.model.mark.Mark; import seng302.server.simulator.parsers.RaceParser; -import seng302.utilities.GeoPoint; +import seng302.model.GeoPoint; import seng302.utilities.GeoUtility; public class Simulator extends Observable implements Runnable { diff --git a/src/main/java/seng302/server/simulator/mark/CompoundMark.java b/src/main/java/seng302/server/simulator/mark/CompoundMark.java deleted file mode 100644 index 489a4a12..00000000 --- a/src/main/java/seng302/server/simulator/mark/CompoundMark.java +++ /dev/null @@ -1,70 +0,0 @@ -package seng302.server.simulator.mark; - -public class CompoundMark { - - private int markID; - private String name; - - private Mark mark1; - private Mark mark2; - - public CompoundMark(int markID, String name) { - this.markID = markID; - this.name = name; - } - - public void addMark(int seqId, Mark mark) { - if (seqId == 1) { - setMark1(mark); - } else if (seqId == 2) { - setMark2(mark); - } - } - - /** - * Prints out compoundMark's info and its marks, good for testing - * @return a string showing its details - */ - @Override - public String toString(){ - if (mark2 == null) - return String.format("CompoundMark: %d (%s), [%s]", - markID, name, mark1.toString()); - return String.format("CompoundMark: %d (%s), [%s; %s]", - markID, name, mark1.toString(), mark2.toString()); - } - - public int getMarkID() { - return markID; - } - - public void setMarkID(int markID) { - this.markID = markID; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Mark getMark1() { - return mark1; - } - - public void setMark1(Mark mark1) { - this.mark1 = mark1; - mark1.setSeqID(1); - } - - public Mark getMark2() { - return mark2; - } - - public void setMark2(Mark mark2) { - this.mark2 = mark2; - mark2.setSeqID(2); - } -} diff --git a/src/main/java/seng302/server/simulator/mark/Mark.java b/src/main/java/seng302/server/simulator/mark/Mark.java deleted file mode 100644 index 0b6f1f3b..00000000 --- a/src/main/java/seng302/server/simulator/mark/Mark.java +++ /dev/null @@ -1,55 +0,0 @@ -package seng302.server.simulator.mark; - -import seng302.utilities.GeoPoint; - -/** - * An abstract class to represent general marks - * Created by Haoming Yin (hyi25) on 17/3/17. - */ -public class Mark extends GeoPoint { - - private int seqID; - private String name; - private int sourceID; - - public Mark(String name, double lat, double lng, int sourceID) { - super(lat, lng); - this.name = name; - this.sourceID = sourceID; - } - - /** - * Prints out mark's info and its geo location, good for testing - * @return a string showing its details - */ - @Override - public String toString() { - return String.format("Mark%d: %s, source: %d, lat: %f, lng: %f", seqID, name, sourceID, getLat(), getLng()); - } - - public int getSeqID() { - return seqID; - } - - public void setSeqID(int seqID) { - this.seqID = seqID; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getSourceID() { - return sourceID; - } - - public void setSourceID(int sourceID) { - this.sourceID = sourceID; - } -} - - diff --git a/src/main/java/seng302/server/simulator/parsers/CourseParser.java b/src/main/java/seng302/server/simulator/parsers/CourseParser.java index 40108983..b7381edf 100644 --- a/src/main/java/seng302/server/simulator/parsers/CourseParser.java +++ b/src/main/java/seng302/server/simulator/parsers/CourseParser.java @@ -8,10 +8,10 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import seng302.server.simulator.mark.CompoundMark; -import seng302.server.simulator.mark.Corner; -import seng302.server.simulator.mark.Mark; -import seng302.server.simulator.mark.RoundingType; +import seng302.model.mark.CompoundMark; +import seng302.server.simulator.Corner; +import seng302.model.mark.Mark; +import seng302.server.simulator.RoundingType; /** * Parses the race xml file to get course details @@ -66,7 +66,7 @@ public class CourseParser extends FileParser { for (int i = 0; i < cMarks.getLength(); i++) { CompoundMark cMark = getCompoundMark(cMarks.item(i)); if (cMark != null) - compoundMarksMap.put(cMark.getMarkID(), cMark); + compoundMarksMap.put(cMark.getId(), cMark); } return compoundMarksMap; @@ -87,7 +87,7 @@ public class CourseParser extends FileParser { for (int i = 0; i < marks.getLength(); i++) { Mark mark = getMark(marks.item(i)); if (mark != null) - cMark.addMark(mark.getSeqID(), mark); + cMark.addSubMarks(mark); } return cMark; } diff --git a/src/main/java/seng302/server/simulator/parsers/RaceParser.java b/src/main/java/seng302/server/simulator/parsers/RaceParser.java index e3fb7674..5e5cccae 100644 --- a/src/main/java/seng302/server/simulator/parsers/RaceParser.java +++ b/src/main/java/seng302/server/simulator/parsers/RaceParser.java @@ -7,7 +7,7 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import seng302.server.simulator.Boat; -import seng302.server.simulator.mark.Corner; +import seng302.server.simulator.Corner; /** * Parses the race xml file to get course details diff --git a/src/main/java/seng302/utilities/GeoUtility.java b/src/main/java/seng302/utilities/GeoUtility.java index 0e17ff1a..6aff3301 100644 --- a/src/main/java/seng302/utilities/GeoUtility.java +++ b/src/main/java/seng302/utilities/GeoUtility.java @@ -1,6 +1,7 @@ package seng302.utilities; import javafx.geometry.Point2D; +import seng302.model.GeoPoint; public class GeoUtility { @@ -43,16 +44,31 @@ public class GeoUtility { * and end up on a heading of 120° */ public static Double getBearing(GeoPoint p1, GeoPoint p2) { + return (getBearingRad(p1, p2) + 360.0) % 360.0; + } + /** + * Calculates the angle between to angular co-ordinates on a sphere in radians. + * + * @param p1 the first geographical position, start point + * @param p2 the second geographical position, end point + * @return the initial bearing in degree from p1 to p2, value range (0 ~ 360 deg.). + * vertical up is 0 deg. horizontal right is 90 deg. + * + * NOTE: + * The final bearing will differ from the initial bearing by varying degrees + * according to distance and latitude (if you were to go from say 35°N,45°E + * (≈ Baghdad) to 35°N,135°E (≈ Osaka), you would start on a heading of 60° + * and end up on a heading of 120° + */ + public static Double getBearingRad(GeoPoint p1, GeoPoint p2) { double dLon = Math.toRadians(p2.getLng() - p1.getLng()); double y = Math.sin(dLon) * Math.cos(Math.toRadians(p2.getLat())); double x = Math.cos(Math.toRadians(p1.getLat())) * Math.sin(Math.toRadians(p2.getLat())) - - Math.sin(Math.toRadians(p1.getLat())) * Math.cos(Math.toRadians(p2.getLat())) * Math.cos(dLon); + - Math.sin(Math.toRadians(p1.getLat())) * Math.cos(Math.toRadians(p2.getLat())) * Math.cos(dLon); - double bearing = Math.toDegrees(Math.atan2(y, x)); - - return (bearing + 360.0) % 360.0; + return Math.toDegrees(Math.atan2(y, x)); } /** diff --git a/src/main/java/seng302/utilities/XMLParser.java b/src/main/java/seng302/utilities/XMLParser.java index 38f6bb0d..b73f9b83 100644 --- a/src/main/java/seng302/utilities/XMLParser.java +++ b/src/main/java/seng302/utilities/XMLParser.java @@ -8,13 +8,11 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import seng302.model.Corner; import seng302.model.Limit; import seng302.model.Yacht; -import seng302.model.mark.GateMark; +import seng302.model.mark.CompoundMark; +import seng302.model.mark.Corner; import seng302.model.mark.Mark; -import seng302.model.mark.MarkType; -import seng302.model.mark.SingleMark; import seng302.model.stream.xml.parser.RaceXMLData; import seng302.model.stream.xml.parser.RegattaXMLData; @@ -249,13 +247,19 @@ public class XMLParser { /** * Extracts course mark data */ - private static List extractCompoundMarks(Element docEle) { - List allMarks = new ArrayList<>(); + private static List extractCompoundMarks(Element docEle) { + List allMarks = new ArrayList<>(); NodeList cMarkList = docEle.getElementsByTagName("Course").item(0).getChildNodes(); + CompoundMark cMark; for (int i = 0; i < cMarkList.getLength(); i++) { Node cMarkNode = cMarkList.item(i); if (cMarkNode.getNodeName().equals("CompoundMark")) { - allMarks.add(createMark(cMarkNode)); + cMark = new CompoundMark( + XMLParser.getNodeAttributeInt(cMarkNode, "CompoundMarkID"), + XMLParser.getNodeAttributeString(cMarkNode, "Name") + ); + cMark.addSubMarks(createMarks(cMarkNode)); + allMarks.add(cMark); } } return allMarks; @@ -264,8 +268,8 @@ public class XMLParser { /** * Creates marks objects from the given node */ - private static Mark createMark(Node compoundMark) { - List subMarks = new ArrayList<>(); + private static List createMarks(Node compoundMark) { + List subMarks = new ArrayList<>(); Integer compoundMarkID = XMLParser.getNodeAttributeInt(compoundMark, "CompoundMarkID"); String cMarkName = XMLParser.getNodeAttributeString(compoundMark, "Name"); @@ -277,17 +281,10 @@ public class XMLParser { String markName = XMLParser.getNodeAttributeString(markNode, "Name"); Double targetLat = XMLParser.getNodeAttributeDouble(markNode, "TargetLat"); Double targetLng = XMLParser.getNodeAttributeDouble(markNode, "TargetLng"); - SingleMark mark = new SingleMark(markName, targetLat, targetLng, sourceID, - compoundMarkID); + Mark mark = new Mark(markName, targetLat, targetLng, sourceID); subMarks.add(mark); } } - if (subMarks.size() == 1) { - return subMarks.get(0); - } else { - return new GateMark( cMarkName, MarkType.OPEN_GATE, subMarks.get(0), subMarks.get(1), - subMarks.get(0).getLatitude(), subMarks.get(0).getLongitude(), compoundMarkID - ); - } + return subMarks; } } \ No newline at end of file diff --git a/src/main/java/seng302/visualiser/GameView.java b/src/main/java/seng302/visualiser/GameView.java index 1cc617dd..59851c80 100644 --- a/src/main/java/seng302/visualiser/GameView.java +++ b/src/main/java/seng302/visualiser/GameView.java @@ -1,6 +1,5 @@ package seng302.visualiser; -import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -11,38 +10,34 @@ import java.util.Map; import javafx.animation.AnimationTimer; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.collections.ObservableList; -import javafx.fxml.FXMLLoader; import javafx.geometry.Point2D; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.image.ImageView; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.GridPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; import javafx.scene.shape.Polygon; import javafx.scene.text.Text; import seng302.model.Colors; +import seng302.model.GeoPoint; import seng302.model.Limit; import seng302.model.Yacht; -import seng302.model.mark.GateMark; +import seng302.model.mark.CompoundMark; import seng302.model.mark.Mark; -import seng302.model.mark.MarkType; -import seng302.model.mark.SingleMark; -import seng302.utilities.GeoPoint; import seng302.utilities.GeoUtility; import seng302.visualiser.fxObjects.AnnotationBox; import seng302.visualiser.fxObjects.BoatObject; -import seng302.visualiser.fxObjects.MarkObject; +import seng302.visualiser.fxObjects.Gate; +import seng302.visualiser.fxObjects.Marker; +import seng302.visualiser.map.Boundary; +import seng302.visualiser.map.CanvasMap; /** * Created by cir27 on 20/07/17. */ public class GameView extends Pane { - private ObservableList gameObjects; - private ImageView mapImage; - private double bufferSize = 50; private double panelWidth = 1260; // it should be 1280 but, minors 40 to cancel the bias. private double panelHeight = 960; @@ -52,23 +47,25 @@ public class GameView extends Pane { private double distanceScaleFactor; private ScaleDirection scaleDirection; - private Mark minLatPoint; - private Mark minLonPoint; - private Mark maxLatPoint; - private Mark maxLonPoint; - private double referencePointX; - private double referencePointY; - private double metersPerPixelX; - private double metersPerPixelY; + private GeoPoint minLatPoint, minLonPoint, maxLatPoint, maxLonPoint; + private double referencePointX, referencePointY; + private double metersPerPixelX, metersPerPixelY; + + private Text fpsDisplay = new Text(); + private Polygon raceBorder = new Polygon(); + + /* Note that if either of these is null then values for it have not been added and the other + should be used as the limits of the map. */ + private List borderPoints; + private List course; + private Map markerObjects; private Map boatObjects = new HashMap<>(); private List annotations = new ArrayList<>(); + private List markSequence; + private ObservableList gameObjects; - private Text fpsDisplay = new Text(); - /* Note that if either of these is null then values for it have not been added and the other - should be used as the limits of the map. */ - private Polygon raceBorder; - private Map markObjects = new HashMap<>(); + private ImageView mapImage = new ImageView(); //FRAME RATE private Double frameRate = 60.0; @@ -86,11 +83,24 @@ public class GameView extends Pane { public GameView () { gameObjects = this.getChildren(); // create image view for map, bind panel size to image - mapImage = new ImageView(); - gameObjects.add(mapImage); mapImage.fitWidthProperty().bind(this.widthProperty()); mapImage.fitHeightProperty().bind(this.heightProperty()); + gameObjects.add(mapImage); + fpsDisplay.setLayoutX(5); + fpsDisplay.setLayoutY(20); + fpsDisplay.setStrokeWidth(2); + gameObjects.add(fpsDisplay); + gameObjects.add(raceBorder); initializeTimer(); + this.widthProperty().addListener(resize -> { + canvasWidth = this.getWidth(); + canvasHeight = this.getHeight(); + if (borderPoints != null) { + updateBorder(borderPoints); + } else if (course != null) { + updateCourse(course, markSequence); + } + }); } private void initializeTimer () { @@ -120,7 +130,7 @@ public class GameView extends Pane { drawFps(frameRate.intValue()); } } - updateGroups(); + boatObjects.forEach((boat, boatObject) -> {}); lastTime = now; } } @@ -128,45 +138,13 @@ public class GameView extends Pane { }; } - public void initializeCanvas() { - drawGoogleMap(); - fpsDisplay.setLayoutX(5); - fpsDisplay.setLayoutY(20); - fpsDisplay.setStrokeWidth(2); - gameObjects.add(fpsDisplay); - gameObjects.add(raceBorder); - - this.widthProperty().addListener(resize -> { - canvasWidth = this.getWidth(); - canvasHeight = this.getHeight(); - fitMarksToCanvas(); - }); - } - - private void switchToFinishScreen() { - try { - // canvas view -> anchor pane -> grid pane -> main view - GridPane gridPane = (GridPane) this.getParent().getParent(); - AnchorPane contentPane = (AnchorPane) gridPane.getParent(); - contentPane.getChildren().removeAll(); - contentPane.getChildren().clear(); - contentPane.getStylesheets().add(getClass().getResource("/css/master.css").toString()); - contentPane.getChildren().addAll( - (Pane) FXMLLoader.load(getClass().getResource("/views/FinishScreenView.fxml"))); - } catch (javafx.fxml.LoadException e) { - System.out.println("[Controller] FXML load exception"); - } catch (IOException e) { - System.out.println("[Controller] IO exception"); - } - } - /** * First find the top right and bottom left points' geo locations, then retrieve * map from google to display on image view. - Haoming 22/5/2017 */ private void drawGoogleMap() { findMetersPerPixel(); - Point2D topLeftPoint = findScaledXY(maxLatPoint.getLatitude(), minLonPoint.getLongitude()); + Point2D topLeftPoint = findScaledXY(maxLatPoint.getLat(), minLonPoint.getLng()); // distance from top left extreme to panel origin (top left corner) double distanceFromTopLeftToOrigin = Math.sqrt( Math.pow(topLeftPoint.getX() * metersPerPixelX, 2) + Math @@ -175,7 +153,7 @@ public class GameView extends Pane { double bearingFromTopLeftToOrigin = Math .toDegrees(Math.atan2(-topLeftPoint.getX(), topLeftPoint.getY())); // the top left extreme - GeoPoint topLeftPos = new GeoPoint(maxLatPoint.getLatitude(), minLonPoint.getLongitude()); + GeoPoint topLeftPos = new GeoPoint(maxLatPoint.getLat(), minLonPoint.getLng()); GeoPoint originPos = GeoUtility .getGeoCoordinate(topLeftPos, bearingFromTopLeftToOrigin, distanceFromTopLeftToOrigin); @@ -196,20 +174,103 @@ public class GameView extends Pane { } /** - * Adds border marks to the canvas, taken from the XML file + * Adds a course to the GameView. The view is scaled accordingly unless a border is set in which + * case the course is added relative ot the border. * - * NOTE: This is quite confusing as objects are grabbed from the XMLParser such as Mark and - * CompoundMark which are named the same as those in the model package but are, however not the - * same, so they do not have things such as a type and must be derived from the number of marks - * in a compound mark etc.. + * @param newCourse the mark objects that make up the course. + */ + public void updateCourse(List newCourse, List sequence) { + course = newCourse; + this.markSequence = sequence; + markerObjects = new HashMap<>(); + Paint colour = Color.BLACK; + //Creates new markers + for (CompoundMark cMark : course) { + //Set start and end colour + if (cMark.getId() == sequence.get(0)) { + colour = Color.GREEN; + } else if (cMark.getId() == sequence.get(sequence.size() - 1)) { + colour = Color.RED; + } + //Create mark dots + for (Mark mark : cMark.getMarks()) { + makeAndBindMarker(mark, colour); + } + //Create gate line + if (cMark.isGate()) { + for (int i = 0; i < cMark.getMarks().size()-1; i++) { + makeAndBindGate( + markerObjects.get(cMark.getSubMark(i)), + markerObjects.get(cMark.getSubMark(i+1)), + colour + ); + } + } + colour = Color.BLACK; + } + //Set X,Y co-ordinates + if (borderPoints == null) { + rescaleRace(new ArrayList<>(markerObjects.keySet())); + } else { + rescaleRace(new ArrayList<>(borderPoints)); + } + //Move the Markers to initial position. + markerObjects.forEach(((mark, marker) -> { + Point2D p2d = findScaledXY(mark.getLat(), mark.getLng()); + marker.setCenterX(p2d.getX()); + marker.setCenterY(p2d.getY()); + })); + } + + /** + * Creates a new Marker and binds it's position to the given Mark. + * + * @param observableMark The mark to bind the marker to. + * @param colour The desired colour of the mark + */ + private void makeAndBindMarker(Mark observableMark, Paint colour) { + Marker marker = new Marker(colour); + markerObjects.put(observableMark, marker); + observableMark.addPositionListener((mark, lat, lon) -> { + Point2D p2d = findScaledXY(lat, lon); + markerObjects.get(mark).setCenterX(p2d.getX()); + markerObjects.get(mark).setCenterY(p2d.getY()); + }); + } + + /** + * Creates a new gate connecting the given marks. + * + * @param m1 The first Mark of the gate. + * @param m2 The second Mark of the gate. + * @param colour The desired colour of the gate. + */ + private void makeAndBindGate(Marker m1, Marker m2, Paint colour) { + Gate gate = new Gate(colour); + gate.startXProperty().bind( + m1.centerXProperty() + ); + gate.startYProperty().bind( + m1.centerYProperty() + ); + gate.endXProperty().bind( + m2.centerXProperty() + ); + gate.endYProperty().bind( + m2.centerYProperty() + ); + } + + /** + * Adds a border to the GameView and rescales to the size of the border, does not rescale if a + * border already exists. Assumes the border is larger than the course. + * + * @param border the race border to be drawn. */ public void updateBorder(List border) { - if (raceBorder == null) { - raceBorder = new Polygon(); - raceBorder.setStroke(new Color(0.0f, 0.0f, 0.74509807f, 1)); - raceBorder.setStrokeWidth(3); - raceBorder.setFill(new Color(0,0,0,0)); - findCanvasScaling(); + if (borderPoints == null) { + borderPoints = border; + rescaleRace(new ArrayList<>(borderPoints)); } List boundaryPoints = new ArrayList<>(); for (Limit limit : border) { @@ -220,42 +281,19 @@ public class GameView extends Pane { raceBorder.getPoints().setAll(boundaryPoints); } - private void updateGroups() { - boatObjects.forEach((boat, boatObject) -> {}); - markObjects.forEach((mark, markObject) -> {}); + /** + * Rescales the race to the size of the window. + * + * @param limitingCoordinates the set of geo points that contains the extremities of the race. + */ + private void rescaleRace(List limitingCoordinates) { + //Check is called once to avoid unnecessarily change the course limits once the race is running + findMinMaxPoint(limitingCoordinates); + double minLonToMaxLon = scaleRaceExtremities(); + calculateReferencePointLocation(minLonToMaxLon); + drawGoogleMap(); } -// private void updateBoatGroup(BoatGroup boatGroup) { -//// 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 { -//// PositionUpdateData positionPacket = movementQueue.take(); -// Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon()); -//// double heading = 360.0 / 0xffff * positionPacket.getHeading(); -// boatGroup.setDestination( -// p2d.getX(), p2d.getY(), heading, positionPacket.getGroundSpeed(), -// positionPacket.getTimeValid(), frameRate); -//// } catch (InterruptedException e){ -//// e.printStackTrace(); -//// } -////// } -//// } -// } -// -// private void updateMarkGroup (long raceId, MarkGroup markGroup) { -// PriorityBlockingQueue movementQueue = StreamParser.markLocations.get(raceId); -// if (movementQueue.size() > 0){ -// try { -// PositionUpdateData positionPacket = movementQueue.take(); -// Point2D p2d = findScaledXY(positionPacket.getLat(), positionPacket.getLon()); -// markGroup.moveMarkTo(p2d.getX(), p2d.getY(), raceId); -// } catch (InterruptedException e) { -// e.printStackTrace(); -// } -// } -// } - /** * Draws all the boats. */ @@ -320,65 +358,27 @@ public class GameView extends Pane { return newAnnotation; } - public void updateCourse(List course) { - for (Mark mark : course) { - if (mark.getMarkType() == MarkType.SINGLE_MARK) { - SingleMark sMark = (SingleMark) mark; - - MarkObject markObject = new MarkObject(sMark, findScaledXY(sMark)); - markObjects.put(sMark, markObject); - } else { - GateMark gMark = (GateMark) mark; - - MarkObject markObject = new MarkObject(gMark, findScaledXY(gMark.getSingleMark1()), - findScaledXY(gMark.getSingleMark2())); //should be 2 objects in the list. -// markObjects.put(markObject.); - } - } -// gameObjects.addAll(markObjects); - } - private void drawFps(int fps){ fpsDisplay.setText(String.format("%d FPS", fps)); } /** - * Calculates x and y location for every marker that fits it to the canvas the race will be - * drawn on. - */ - private void fitMarksToCanvas() { - //Check is called once to avoid unnecessarily change the course limits once the race is running - findMinMaxPoint(); - double minLonToMaxLon = scaleRaceExtremities(); - calculateReferencePointLocation(minLonToMaxLon); - //givePointsXY(); -// updateBorder(); - } - - - /** - * Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the marker - * with the leftmost marker, rightmost marker, southern most marker and northern most marker + * Sets the class variables minLatPoint, maxLatPoint, minLonPoint, maxLonPoint to the point + * with the leftmost point, rightmost point, southern most point and northern most point * respectively. */ - private void findMinMaxPoint() { - List sortedPoints = new ArrayList<>(); -// for (Limit limit : ) { -// sortedPoints.add(limit); -// } - 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(), minLatMark.getSeqID()); - maxLatPoint = new SingleMark(maxLatMark.toString(), maxLatMark.getLat(), maxLatMark.getLng(), maxLatMark.getSeqID(), minLatMark.getSeqID()); + private void findMinMaxPoint(List points) { + List sortedPoints = new ArrayList<>(points); + sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLat)); + minLatPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng()); + GeoPoint maxLat = sortedPoints.get(sortedPoints.size()-1); + maxLatPoint = new GeoPoint(maxLat.getLat(), maxLat.getLng()); - 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(), minLonMark.getSeqID()); - maxLonPoint = new SingleMark(maxLonMark.toString(), maxLonMark.getLat(), maxLonMark.getLng(), maxLonMark.getSeqID(), minLonMark.getSeqID()); - if (maxLonPoint.getLongitude() - minLonPoint.getLongitude() > 180) { + sortedPoints.sort(Comparator.comparingDouble(GeoPoint::getLng)); + minLonPoint = new GeoPoint(sortedPoints.get(0).getLat(), sortedPoints.get(0).getLng()); + GeoPoint maxLon = sortedPoints.get(sortedPoints.size()-1); + maxLonPoint = new GeoPoint(maxLon.getLat(), maxLon.getLng()); + if (maxLonPoint.getLng() - minLonPoint.getLng() > 180) { horizontalInversion = true; } } @@ -391,25 +391,29 @@ public class GameView extends Pane { * maximum longitude. */ private void calculateReferencePointLocation(double minLonToMaxLon) { - Mark referencePoint = minLatPoint; + GeoPoint referencePoint = minLatPoint; double referenceAngle; if (scaleDirection == ScaleDirection.HORIZONTAL) { - referenceAngle = Math.abs(Mark.calculateHeadingRad(referencePoint, minLonPoint)); - referencePointX = bufferSize + distanceScaleFactor * Math.sin(referenceAngle) * Mark.calculateDistance(referencePoint, minLonPoint); - - referenceAngle = Math.abs(Mark.calculateHeadingRad(referencePoint, maxLatPoint)); + referenceAngle = Math.abs( + GeoUtility.getBearingRad(referencePoint, minLonPoint) + ); + referencePointX = bufferSize + distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility.getDistance(referencePoint, minLonPoint); + referenceAngle = Math.abs(GeoUtility.getDistance(referencePoint, maxLatPoint)); referencePointY = canvasHeight - (bufferSize + bufferSize); - referencePointY -= distanceScaleFactor * Math.cos(referenceAngle) * Mark.calculateDistance(referencePoint, maxLatPoint); + referencePointY -= distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility.getDistance(referencePoint, maxLatPoint); referencePointY = referencePointY / 2; referencePointY += bufferSize; - referencePointY += distanceScaleFactor * Math.cos(referenceAngle) * Mark.calculateDistance(referencePoint, maxLatPoint); + referencePointY += distanceScaleFactor * Math.cos(referenceAngle) * GeoUtility.getDistance(referencePoint, maxLatPoint); } else { referencePointY = canvasHeight - bufferSize; - - referenceAngle = Math.abs(Mark.calculateHeadingRad(referencePoint, minLonPoint)); + referenceAngle = Math.abs( + Math.toRadians( + GeoUtility.getDistance(referencePoint, minLonPoint) + ) + ); referencePointX = bufferSize; - referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * Mark.calculateDistance(referencePoint, minLonPoint); + referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * GeoUtility.getDistance(referencePoint, minLonPoint); referencePointX += ((canvasWidth - (bufferSize + bufferSize)) - (minLonToMaxLon * distanceScaleFactor)) / 2; } if(horizontalInversion) { @@ -424,18 +428,21 @@ public class GameView extends Pane { */ private double scaleRaceExtremities() { - double vertAngle = Math.abs(Mark.calculateHeadingRad(minLatPoint, maxLatPoint)); + double vertAngle = Math.abs( + GeoUtility.getBearingRad(minLatPoint, maxLatPoint) + ); double vertDistance = - Math.cos(vertAngle) * Mark.calculateDistance(minLatPoint, maxLatPoint); - double horiAngle = Mark.calculateHeadingRad(minLonPoint, maxLonPoint); - + Math.cos(vertAngle) * GeoUtility.getDistance(minLatPoint, maxLatPoint); + double horiAngle = Math.abs( + GeoUtility.getBearingRad(minLonPoint, maxLonPoint) + ); if (horiAngle <= (Math.PI / 2)) { horiAngle = (Math.PI / 2) - horiAngle; } else { horiAngle = horiAngle - (Math.PI / 2); } double horiDistance = - Math.cos(horiAngle) * Mark.calculateDistance(minLonPoint, maxLonPoint); + Math.cos(horiAngle) * GeoUtility.getDistance(minLonPoint, maxLonPoint); double vertScale = (canvasHeight - (bufferSize + bufferSize)) / vertDistance; @@ -449,22 +456,22 @@ public class GameView extends Pane { return horiDistance; } - private Point2D findScaledXY(Mark unscaled) { - return findScaledXY(unscaled.getLatitude(), unscaled.getLongitude()); + private Point2D findScaledXY(GeoPoint unscaled) { + return findScaledXY(unscaled.getLat(), unscaled.getLng()); } - public Point2D findScaledXY (double unscaledLat, double unscaledLon) { + private Point2D findScaledXY (double unscaledLat, double unscaledLon) { double distanceFromReference; double angleFromReference; double xAxisLocation = referencePointX; double yAxisLocation = referencePointY; - angleFromReference = Mark - .calculateHeadingRad(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat, - unscaledLon); - distanceFromReference = Mark - .calculateDistance(minLatPoint.getLatitude(), minLatPoint.getLongitude(), unscaledLat, - unscaledLon); + angleFromReference = GeoUtility.getBearingRad( + minLatPoint, new GeoPoint(unscaledLat, unscaledLon) + ); + distanceFromReference = GeoUtility.getDistance( + minLatPoint, new GeoPoint(unscaledLat, unscaledLon) + ); if (angleFromReference >= 0 && angleFromReference <= Math.PI / 2) { xAxisLocation += Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); yAxisLocation -= Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); @@ -492,14 +499,14 @@ public class GameView extends Pane { */ private void findMetersPerPixel() { Point2D p1, p2; - Mark m1, m2; + GeoPoint g1, g2; double theta, distance, dx, dy, dHorizontal, dVertical; - 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); - distance = Mark.calculateDistance(m1, m2); + g1 = new GeoPoint(maxLatPoint.getLat(), minLonPoint.getLng()); + g2 = new GeoPoint(minLatPoint.getLat(), maxLatPoint.getLng()); + p1 = findScaledXY(new GeoPoint(maxLatPoint.getLat(), minLonPoint.getLng())); + p2 = findScaledXY(new GeoPoint(minLatPoint.getLat(), maxLatPoint.getLng())); + theta = GeoUtility.getBearingRad(g1, g2); + distance = GeoUtility.getDistance(g1, g2); dHorizontal = Math.abs(Math.sin(theta) * distance); dVertical = Math.abs(Math.cos(theta) * distance); dx = Math.abs(p1.getX() - p2.getX()); diff --git a/src/main/java/seng302/visualiser/fxObjects/BoatObject.java b/src/main/java/seng302/visualiser/fxObjects/BoatObject.java index 6a7bdb7a..571da4a4 100644 --- a/src/main/java/seng302/visualiser/fxObjects/BoatObject.java +++ b/src/main/java/seng302/visualiser/fxObjects/BoatObject.java @@ -344,4 +344,6 @@ public class BoatObject extends Group { // boatAnnotations.setAsPlayer(); // isPlayer = true; } + + public void updateTrajectory(heading, velocity, scaleFactor) } \ No newline at end of file diff --git a/src/main/java/seng302/visualiser/fxObjects/CourseBorder.java b/src/main/java/seng302/visualiser/fxObjects/CourseBorder.java new file mode 100644 index 00000000..3496d3e6 --- /dev/null +++ b/src/main/java/seng302/visualiser/fxObjects/CourseBorder.java @@ -0,0 +1,15 @@ +package seng302.visualiser.fxObjects; + +import javafx.scene.paint.Color; +import javafx.scene.shape.Polygon; + +/** + * Polygon with default course border settings. + */ +public class CourseBorder extends Polygon { + public CourseBorder() { + this.setStroke(new Color(0.0f, 0.0f, 0.74509807f, 1)); + this.setStrokeWidth(3); + this.setFill(new Color(0,0,0,0)); + } +} diff --git a/src/main/java/seng302/visualiser/fxObjects/Gate.java b/src/main/java/seng302/visualiser/fxObjects/Gate.java new file mode 100644 index 00000000..0e6c99e3 --- /dev/null +++ b/src/main/java/seng302/visualiser/fxObjects/Gate.java @@ -0,0 +1,20 @@ +package seng302.visualiser.fxObjects; + +import javafx.scene.paint.Paint; +import javafx.scene.shape.Line; + +/** + * Visual object representing a gate, intended to connect two mark objects. + */ +public class Gate extends Line { + + public Gate () { + super.setStrokeWidth(2); + super.getStrokeDashArray().setAll(2d, 5d); + } + + public Gate (Paint colour) { + this(); + super.setStroke(colour); + } +} diff --git a/src/main/java/seng302/visualiser/fxObjects/MarkObject.java b/src/main/java/seng302/visualiser/fxObjects/MarkObject.java deleted file mode 100644 index 21515cad..00000000 --- a/src/main/java/seng302/visualiser/fxObjects/MarkObject.java +++ /dev/null @@ -1,167 +0,0 @@ -package seng302.visualiser.fxObjects; - -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; -import seng302.model.mark.GateMark; -import seng302.model.mark.Mark; -import seng302.model.mark.MarkType; -import seng302.model.mark.SingleMark; - -/** - * Grouping of javaFX objects needed to represent a Mark on screen. - */ -public class MarkObject extends Group { - - private static int MARK_RADIUS = 5; - private static int LINE_THICKNESS = 2; - private static double DASHED_GAP_LEN = 2d; - private static double DASHED_LINE_LEN = 5d; - - private List marks = new ArrayList<>(); - private Mark mainMark; - - /** - * Constructor for singleMark groups - * @param mark - * @param points - */ - public MarkObject(SingleMark mark, Point2D points) { - marks.add(mark); - mainMark = mark; - Color color = Color.BLACK; - if (mark.getName().equals("Start")){ - color = Color.GREEN; - } else if (mark.getName().equals("Finish")){ - color = Color.RED; - } - Circle markCircle; - markCircle = new Circle( - points.getX(), - points.getY(), - MARK_RADIUS, - color - ); - super.getChildren().add(markCircle); - } - - public void addLaylines(Line line1, Line line2) { - - super.getChildren().addAll(line1, line2); - } - - - 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 MarkObject(GateMark mark, Point2D points1, Point2D points2) { - marks.add(mark.getSingleMark1()); - marks.add(mark.getSingleMark2()); - mainMark = mark; - Color color = Color.BLACK; - if (mark.getName().equals("Start")){ - color = Color.GREEN; - } else if (mark.getName().equals("Finish")){ - color = Color.RED; - } - Circle markCircle; - markCircle = new Circle( - points1.getX(), - points1.getY(), - MARK_RADIUS, - color - ); - super.getChildren().add(markCircle); - - markCircle = new Circle( - points2.getX(), - points2.getY(), - MARK_RADIUS, - color - ); - super.getChildren().add(markCircle); - Line line = new Line( - points1.getX(), - points1.getY(), - points2.getX(), - points2.getY() - ); - line.setStrokeWidth(LINE_THICKNESS); - line.setStroke(color); - if (mark.getMarkType() == MarkType.OPEN_GATE) { - line.getStrokeDashArray().addAll(DASHED_GAP_LEN, DASHED_LINE_LEN); - } - super.getChildren().add(line); - - } - - public void moveMarkTo (double x, double y, long raceId) - { - if (mainMark.getMarkType() == MarkType.SINGLE_MARK) { - Circle markCircle = (Circle) super.getChildren().get(0); - //One of the test streams produced frequent, jittery movements. Added this as a fix. - if (Math.abs(markCircle.getCenterX() - x) > 5 || Math.abs(markCircle.getCenterY() - y) > 5) { - markCircle.setCenterX(x); - markCircle.setCenterY(y); - } - } else { - Circle markCircle1 = (Circle) super.getChildren().get(0); - Circle markCircle2 = (Circle) super.getChildren().get(1); - Line connectingLine = (Line) super.getChildren().get(2); - if (marks.get(0).getId() == raceId) { - if (Math.abs(markCircle1.getCenterX() - x) > 5 || Math.abs(markCircle1.getCenterY() - y) > 5) { - markCircle1.setCenterX(x); - markCircle1.setCenterY(y); - connectingLine.setStartX(markCircle1.getCenterX()); - connectingLine.setStartY(markCircle1.getCenterY()); - } - } else if (marks.get(1).getId() == raceId) { - if (Math.abs(markCircle2.getCenterX() - x) > 5 || Math.abs(markCircle2.getCenterY() - y) > 5) { - markCircle2.setCenterX(x); - markCircle2.setCenterY(y); - connectingLine.setEndX(markCircle2.getCenterX()); - connectingLine.setEndY(markCircle2.getCenterY()); - } - } - } - } - - public boolean hasRaceId (int... raceIds) { - for (int id : raceIds) - for (Mark mark : marks) - if (id == mark.getId()) - return true; - return false; - } - - public long[] getRaceIds () { - long[] idArray = new long[marks.size()]; - int i = 0; - for (Mark mark : marks) - idArray[i++] = mark.getId(); - return idArray; - } - - public Mark getMainMark() { - return mainMark; - } -} \ No newline at end of file diff --git a/src/main/java/seng302/visualiser/fxObjects/Marker.java b/src/main/java/seng302/visualiser/fxObjects/Marker.java new file mode 100644 index 00000000..5697f5ef --- /dev/null +++ b/src/main/java/seng302/visualiser/fxObjects/Marker.java @@ -0,0 +1,19 @@ +package seng302.visualiser.fxObjects; + +import javafx.scene.paint.Paint; +import javafx.scene.shape.Circle; + +/** + * Visual object for a mark. + */ +public class Marker extends Circle { + + public Marker() { + super.setRadius(5); + } + + public Marker(Paint colour) { + this(); + super.setFill(colour); + } +} \ No newline at end of file diff --git a/src/main/java/seng302/visualiser/map/Boundary.java b/src/main/java/seng302/visualiser/map/Boundary.java index 53fb8d06..21f2661d 100644 --- a/src/main/java/seng302/visualiser/map/Boundary.java +++ b/src/main/java/seng302/visualiser/map/Boundary.java @@ -7,11 +7,11 @@ package seng302.visualiser.map; * * Created by Haoming on 10/5/17 */ -class Boundary { +public class Boundary { private double northLat, eastLng, southLat, westLng; - Boundary(double northLat, double eastLng, double southLat, double westLng) { + public Boundary(double northLat, double eastLng, double southLat, double westLng) { this.northLat = northLat; this.eastLng = eastLng; this.southLat = southLat; diff --git a/src/main/java/seng302/visualiser/map/CanvasMap.java b/src/main/java/seng302/visualiser/map/CanvasMap.java index 76b28544..e79805e4 100644 --- a/src/main/java/seng302/visualiser/map/CanvasMap.java +++ b/src/main/java/seng302/visualiser/map/CanvasMap.java @@ -4,7 +4,7 @@ import java.net.URL; import javafx.geometry.Point2D; import javafx.scene.image.Image; import javax.net.ssl.HttpsURLConnection; -import seng302.utilities.GeoPoint; +import seng302.model.GeoPoint; /** * CanvasMap retrieves a map image with given geo boundary from Google Map server. @@ -22,12 +22,12 @@ public class CanvasMap { private String KEY = "AIzaSyC-5oOShMCY5Oy_9L7guYMPUPFHDMr37wE"; - CanvasMap(Boundary boundary) { + public CanvasMap(Boundary boundary) { this.boundary = boundary; calculateOptimalMapSize(); } - Image getMapImage() { + public Image getMapImage() { try { URL url = new URL(getRequest()); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); diff --git a/src/main/java/seng302/visualiser/map/MercatorProjection.java b/src/main/java/seng302/visualiser/map/MercatorProjection.java index 864c4c06..3f86e628 100644 --- a/src/main/java/seng302/visualiser/map/MercatorProjection.java +++ b/src/main/java/seng302/visualiser/map/MercatorProjection.java @@ -1,7 +1,7 @@ package seng302.visualiser.map; import javafx.geometry.Point2D; -import seng302.utilities.GeoPoint; +import seng302.model.GeoPoint; /** * An utility class useful to convert between Geo locations and Mercator projection diff --git a/src/test/java/seng302/models/YachtTest.java b/src/test/java/seng302/models/YachtTest.java index eed1eb93..ba6de8c0 100644 --- a/src/test/java/seng302/models/YachtTest.java +++ b/src/test/java/seng302/models/YachtTest.java @@ -5,7 +5,7 @@ import java.util.List; import org.junit.Before; import seng302.model.PolarTable; import seng302.model.Yacht; -import seng302.utilities.GeoPoint; +import seng302.model.GeoPoint; public class YachtTest { diff --git a/src/test/java/seng302/server/simulator/GeoUtilityTest.java b/src/test/java/seng302/server/simulator/GeoUtilityTest.java index ffa4b977..b426f606 100644 --- a/src/test/java/seng302/server/simulator/GeoUtilityTest.java +++ b/src/test/java/seng302/server/simulator/GeoUtilityTest.java @@ -3,7 +3,7 @@ package seng302.server.simulator; import static org.junit.Assert.assertEquals; import org.junit.Test; -import seng302.utilities.GeoPoint; +import seng302.model.GeoPoint; import seng302.utilities.GeoUtility; /** diff --git a/src/test/java/seng302/visualiser/map/MercatorProjectionTest.java b/src/test/java/seng302/visualiser/map/MercatorProjectionTest.java index 174d916f..03dcaccd 100644 --- a/src/test/java/seng302/visualiser/map/MercatorProjectionTest.java +++ b/src/test/java/seng302/visualiser/map/MercatorProjectionTest.java @@ -3,7 +3,7 @@ package seng302.visualiser.map; import static org.junit.Assert.assertEquals; import org.junit.Test; -import seng302.utilities.GeoPoint; +import seng302.model.GeoPoint; /** * Unit test for Mercator Project class.