package visualiser.model; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.scene.paint.Color; import network.Messages.Enums.BoatStatusEnum; import org.geotools.referencing.GeodeticCalculator; import shared.model.Boat; import shared.model.GPSCoordinate; import java.awt.geom.Point2D; import java.time.ZonedDateTime; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; /** * Represents a Boat on the visualiser side of a race. * This adds visualiser specific functionality to a boat. * This class is used to represent and store information about a boat which may * travel around in a race. It is displayed on the * {@link ResizableRaceCanvas ResizableRaceCanvas} via the * {@link visualiser.Controllers.RaceController RaceController}. */ public class VisualiserBoat extends Boat { /** * The collection of trackpoints generated for the boat. */ private final Queue track = new ConcurrentLinkedQueue<>(); private long nextValidTime = 0; private ZonedDateTime timeSinceLastMark; /** * The boat's color. */ private Color color; /** * Boat initializer which keeps all of the information of the boat. * * @param sourceID The source ID of the boat. * @param name Name of the Boat. * @param abbrev The team/country abbreviation of the boat. * @param color The color of the boat. */ public VisualiserBoat(int sourceID, String name, String abbrev, Color color) { super(sourceID, name, abbrev); this.color = color; } /** * Constructs a mock boat object from a given boat and polars table. * * @param boat The boat to convert into a MockBoat. * @param color The color of the boat. */ public VisualiserBoat(Boat boat, Color color) { super(boat.getSourceID(), boat.getName(), boat.getCountry()); this.color = color; } /** * Returns the position of the end of the boat's wake, which is 180 degrees * from the boat's heading, and whose length is proportional to the boat's * speed. * * @return GPSCoordinate of wake endpoint. */ public GPSCoordinate getWake() { double reverseHeading = getBearing().degrees() - 180; double wakeScale = 5; double distance = wakeScale * getCurrentSpeed(); GeodeticCalculator calc = new GeodeticCalculator(); calc.setStartingGeographicPoint( new Point2D.Double(getCurrentPosition().getLongitude(), getCurrentPosition().getLatitude()) ); calc.setDirection(reverseHeading, distance); Point2D endpoint = calc.getDestinationGeographicPoint(); return new GPSCoordinate(endpoint.getY(), endpoint.getX()); } /** * Adds a new point to boat's track. * @param coordinate of point on track * @see TrackPoint */ public void addTrackPoint(GPSCoordinate coordinate) { Boolean added = System.currentTimeMillis() >= nextValidTime; long currentTime = System.currentTimeMillis(); if (added && (this.getStatus() == BoatStatusEnum.RACING)) { float trackPointTimeInterval = 5000; nextValidTime = currentTime + (long) trackPointTimeInterval; int TRACK_POINT_LIMIT = 10; track.add(new TrackPoint(coordinate, currentTime, TRACK_POINT_LIMIT * (long) trackPointTimeInterval)); } } /** * Returns the boat's sampled track between start of race and current time. * @return queue of track points * @see TrackPoint */ public Queue getTrack() { return track; } /** * Returns the color of the boat. * @return The color of the boat. */ public Color getColor() { return color; } /** * Print method prints the name of the boat * * @return Name of the boat. */ public String toString() { return getName(); } public ZonedDateTime getTimeSinceLastMark() { return timeSinceLastMark; } public void setTimeSinceLastMark(ZonedDateTime timeSinceLastMark) { this.timeSinceLastMark = timeSinceLastMark; } public String getFormattedEstTime() { if (getEstimatedTime() < 0) { return " -"; } if (getEstimatedTime() <= 60) { return " " + getEstimatedTime() + "s"; } else { long seconds = getEstimatedTime() % 60; long minutes = (getEstimatedTime() - seconds) / 60; return String.format(" %dm %ds", minutes, seconds); } } }