Merge branch 'story_51_rounding_fix' into 'master'

Story 51 rounding fix

Acceptance criteria:

- The layout must be sent over the API in a race.xml message, plus boat location messages for the marks as required, before any other boat location messages are sent.
- Each mark must be defined using boat "SourceID"s (see definition of race.xml) so their positions can be adjusted later.
- The course must meet the requirements given in the AC35 Protocol.
- The course limits should be set to clear all marks by at least several boat-lengths.
- The entire race area within the limits must be on the water.


(Work with rounding)

Tested by playing the first map with a variety of rotation angles.

See merge request !43
main
Fan-Wu Yang 8 years ago
commit 54fe37fd3a

1
.gitignore vendored

@ -183,3 +183,4 @@ local.properties
# IntelliJDEA ignore # IntelliJDEA ignore
*.iml *.iml
dedicatedServer/.idea/ dedicatedServer/.idea/
settings/keyBindings.xml

@ -9,6 +9,7 @@ import mock.model.SourceIdAllocator;
import mock.model.commandFactory.CompositeCommand; import mock.model.commandFactory.CompositeCommand;
import mock.model.wind.ShiftingWindGenerator; import mock.model.wind.ShiftingWindGenerator;
import mock.model.wind.WindGenerator; import mock.model.wind.WindGenerator;
import mock.xml.RaceXMLCreator;
import network.Messages.LatestMessages; import network.Messages.LatestMessages;
import shared.dataInput.*; import shared.dataInput.*;
import shared.enums.XMLFileType; import shared.enums.XMLFileType;
@ -19,7 +20,7 @@ import shared.exceptions.XMLReaderException;
import shared.model.Bearing; import shared.model.Bearing;
import shared.model.Constants; import shared.model.Constants;
import java.io.IOException; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
@ -72,6 +73,7 @@ public class Event {
/** /**
* Constructs an event, using various XML files. * Constructs an event, using various XML files.
* @param singlePlayer Whether or not to create a single player event. * @param singlePlayer Whether or not to create a single player event.
* @param mapIndex Specifies which map to use.
* @throws EventConstructionException Thrown if we cannot create an Event for any reason. * @throws EventConstructionException Thrown if we cannot create an Event for any reason.
*/ */
public Event(boolean singlePlayer, int mapIndex) throws EventConstructionException { public Event(boolean singlePlayer, int mapIndex) throws EventConstructionException {
@ -105,17 +107,18 @@ public class Event {
//Read XML files. //Read XML files.
try { try {
//this.raceXML = RaceXMLCreator.alterRaceToWind(raceXMLFile, 90);
this.raceXML = XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8); //this.raceXML = XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8);
this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8));
this.raceXML = RaceXMLCreator.alterRaceToWind(this.raceXML, XMLFileType.Contents, 300, false);
if(mapIndex==4){ if(mapIndex==4){
this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8), 1000, 5000); this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8), 1000, 5000);
} else {
this.raceXML = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString(raceXMLFile, StandardCharsets.UTF_8));
} }
this.boatXML = XMLReader.readXMLFileToString(boatsXMLFile, StandardCharsets.UTF_8); this.boatXML = XMLReader.readXMLFileToString(boatsXMLFile, StandardCharsets.UTF_8);
this.regattaXML = XMLReader.readXMLFileToString(regattaXMLFile, StandardCharsets.UTF_8); this.regattaXML = XMLReader.readXMLFileToString(regattaXMLFile, StandardCharsets.UTF_8);
} catch (XMLReaderException e) { } catch (XMLReaderException | InvalidRaceDataException e) {
throw new EventConstructionException("Could not read XML files.", e); throw new EventConstructionException("Could not read XML files.", e);
} }
@ -196,7 +199,6 @@ public class Event {
//TODO remove this after demo on 18th august!
/** /**
* Sets the xml description of the race to show the race was created now, and starts in 4 minutes * Sets the xml description of the race to show the race was created now, and starts in 4 minutes
* @param raceXML The race.xml contents. * @param raceXML The race.xml contents.
@ -213,8 +215,6 @@ public class Event {
long secondsToAdd = millisecondsToAdd / 1000; long secondsToAdd = millisecondsToAdd / 1000;
//Scale the time using our time scalar.
secondsToAdd = secondsToAdd / Constants.RaceTimeScale;
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ"); DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
ZonedDateTime creationTime = ZonedDateTime.now(); ZonedDateTime creationTime = ZonedDateTime.now();

@ -3,18 +3,13 @@ package mock.model;
import mock.model.commandFactory.ActiveObserverCommand; import mock.model.commandFactory.ActiveObserverCommand;
import mock.model.commandFactory.ObserverCommand; import mock.model.commandFactory.ObserverCommand;
import mock.model.wind.WindGenerator; import mock.model.wind.WindGenerator;
import javafx.animation.AnimationTimer;
import mock.model.collider.ColliderRegistry; import mock.model.collider.ColliderRegistry;
import mock.xml.*;
import network.Messages.BoatLocation;
import network.Messages.BoatStatus;
import network.Messages.Enums.BoatStatusEnum; import network.Messages.Enums.BoatStatusEnum;
import network.Messages.Enums.RaceStatusEnum; import network.Messages.Enums.RaceStatusEnum;
import shared.dataInput.BoatDataSource; import shared.dataInput.BoatDataSource;
import shared.dataInput.RaceDataSource; import shared.dataInput.RaceDataSource;
import shared.dataInput.RegattaDataSource; import shared.dataInput.RegattaDataSource;
import shared.exceptions.BoatNotFoundException; import shared.exceptions.BoatNotFoundException;
import shared.enums.RoundingType;
import shared.model.*; import shared.model.*;
import shared.model.Bearing; import shared.model.Bearing;
@ -126,6 +121,7 @@ public class MockRace extends RaceState {
//Construct a MockBoat using the Boat and Polars. //Construct a MockBoat using the Boat and Polars.
MockBoat mockBoat = new MockBoat(boat, polars); MockBoat mockBoat = new MockBoat(boat, polars);
mockBoat.setCurrentLeg(this.getLegs().get(0)); mockBoat.setCurrentLeg(this.getLegs().get(0));
mockBoat.setEstimatedTimeAtNextMark(this.getRaceClock().getCurrentTime());
//Update participant list. //Update participant list.
getRaceDataSource().getParticipants().add(sourceID); getRaceDataSource().getParticipants().add(sourceID);
@ -371,13 +367,12 @@ public class MockRace extends RaceState {
if (!finish && totalElapsedMilliseconds >= updatePeriodMilliseconds) { if (!finish && totalElapsedMilliseconds >= updatePeriodMilliseconds) {
checkPosition(boat, totalElapsedMilliseconds);
if(boat.isVelocityDefault()) setBoatSpeed(boat); if(boat.isVelocityDefault()) setBoatSpeed(boat);
//Calculates the distance travelled, in meters, in the current timeslice. //Calculates the distance travelled, in meters, in the current timeslice.
double distanceTravelledMeters = boat.calculateMetersTravelled(updatePeriodMilliseconds) * this.scaleFactor; double distanceTravelledMeters = boat.calculateMetersTravelled(updatePeriodMilliseconds) * this.scaleFactor;
checkPosition(boat, totalElapsedMilliseconds);
//Move the boat forwards that many meters, and advances its time counters by enough milliseconds. //Move the boat forwards that many meters, and advances its time counters by enough milliseconds.
boat.moveForwards(distanceTravelledMeters); boat.moveForwards(distanceTravelledMeters);
boat.setTimeSinceTackChange(boat.getTimeSinceTackChange() + updatePeriodMilliseconds); boat.setTimeSinceTackChange(boat.getTimeSinceTackChange() + updatePeriodMilliseconds);
@ -483,22 +478,28 @@ public class MockRace extends RaceState {
/** /**
* Checks to be run on boats rounding marks on the port side * Checks to be run on boats rounding marks on the port side
* @param boat the boat that is rounding a mark * @param boat the boat that is rounding a mark
* @param roundingChecks the checks to run * @param roundingData The data for the current leg's rounding.
* @param legBearing the direction of the leg
*/ */
private void boatRoundingCheckPort(MockBoat boat, List<GPSCoordinate> roundingChecks, Bearing legBearing) { private void boatRoundingCheckPort(MockBoat boat, MarkRoundingData roundingData) {
//boats must pass all checks in order to round a mark //boats must pass all checks in order to round a mark
//boolean for if boat has to/needs to pass through a gate //boolean for if boat has to/needs to pass through a gate
boolean gateCheck = boat.getCurrentLeg().getEndCompoundMark().getMark2() == null || boat.isBetweenGate(boat.getCurrentLeg().getEndCompoundMark()); boolean gateCheck = boat.getCurrentLeg().getEndCompoundMark().getMark2() == null || boat.isBetweenGate(boat.getCurrentLeg().getEndCompoundMark());
Mark roundingMark = boat.getCurrentLeg().getEndCompoundMark().getMarkForRounding(legBearing);
switch (boat.getRoundingStatus()) { switch (boat.getRoundingStatus()) {
case 0://hasn't started rounding case 0://hasn't started rounding
if (boat.isPortSide(roundingMark) && if (boat.isPortSide(roundingData.getMarkToRound()) &&
GPSCoordinate.passesLine(roundingMark.getPosition(), GPSCoordinate.passesLine(
roundingChecks.get(0), boat.getPosition(), legBearing) && roundingData.getMarkToRound().getPosition(),
gateCheck && boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(0)))) { roundingData.getRoundCheck1(),
boat.getPosition(),
roundingData.getLegBearing()) &&
gateCheck &&
boat.isBetweenGate(
roundingData.getMarkToRound(),
Mark.tempMark(roundingData.getRoundCheck1())) ) {
boat.increaseRoundingStatus(); boat.increaseRoundingStatus();
if (boat.getCurrentLeg().getLegNumber() + 2 >= getLegs().size()){ if (boat.getCurrentLeg().getLegNumber() + 2 >= getLegs().size()){
//boat has finished race //boat has finished race
@ -507,11 +508,18 @@ public class MockRace extends RaceState {
} }
break; break;
case 1://has been parallel to the mark; case 1://has been parallel to the mark;
if (boat.isPortSide(roundingMark) && if (boat.isPortSide(roundingData.getMarkToRound()) &&
GPSCoordinate.passesLine(roundingMark.getPosition(), GPSCoordinate.passesLine(
roundingChecks.get(1), boat.getPosition(), roundingData.getMarkToRound().getPosition(),
Bearing.fromDegrees(legBearing.degrees() - 90)) &&//negative 90 from bearing because of port rounding roundingData.getRoundCheck2(),
boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(1)))) { boat.getPosition(),
Bearing.fromDegrees(
GPSCoordinate.calculateBearing(
roundingData.getMarkToRound().getPosition(),
roundingData.getRoundCheck2()).degrees() - 90)) &&//negative 90 from bearing because of port rounding
boat.isBetweenGate(
roundingData.getMarkToRound(),
Mark.tempMark(roundingData.getRoundCheck2()))) {
boat.increaseRoundingStatus(); boat.increaseRoundingStatus();
} }
break; break;
@ -527,23 +535,27 @@ public class MockRace extends RaceState {
/** /**
* Checks to be run on boats rounding marks on the starboard side * Checks to be run on boats rounding marks on the starboard side
* @param boat the boat that is rounding a mark * @param boat the boat that is rounding a mark
* @param roundingChecks the checks to run * @param roundingData The data for the current leg's rounding.
* @param legBearing the direction of the leg
*/ */
private void boatRoundingCheckStarboard(MockBoat boat, List<GPSCoordinate> roundingChecks, Bearing legBearing){ private void boatRoundingCheckStarboard(MockBoat boat, MarkRoundingData roundingData){
//boats must pass all checks in order to round a mark //boats must pass all checks in order to round a mark
//boolean for if boat has to/needs to pass through a gate //boolean for if boat has to/needs to pass through a gate
boolean gateCheck = boat.getCurrentLeg().getEndCompoundMark().getMark2() == null || boat.isBetweenGate(boat.getCurrentLeg().getEndCompoundMark()); boolean gateCheck = boat.getCurrentLeg().getEndCompoundMark().getMark2() == null || boat.isBetweenGate(boat.getCurrentLeg().getEndCompoundMark());
Mark roundingMark = boat.getCurrentLeg().getEndCompoundMark().getMarkForRounding(legBearing);
switch (boat.getRoundingStatus()) { switch (boat.getRoundingStatus()) {
case 0://hasn't started rounding case 0://hasn't started rounding
if (boat.isStarboardSide(roundingMark) && if (boat.isStarboardSide(roundingData.getMarkToRound()) &&
GPSCoordinate.passesLine(roundingMark.getPosition(), GPSCoordinate.passesLine(
roundingChecks.get(0), boat.getPosition(), legBearing) && roundingData.getMarkToRound().getPosition(),
roundingData.getRoundCheck1(),
boat.getPosition(),
roundingData.getLegBearing()) &&
gateCheck && gateCheck &&
boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(0)))) { boat.isBetweenGate(
roundingData.getMarkToRound(),
Mark.tempMark(roundingData.getRoundCheck1()))) {
boat.increaseRoundingStatus(); boat.increaseRoundingStatus();
if (boat.getCurrentLeg().getLegNumber() + 2 >= getLegs().size()){ if (boat.getCurrentLeg().getLegNumber() + 2 >= getLegs().size()){
//boat has finished race //boat has finished race
@ -552,10 +564,18 @@ public class MockRace extends RaceState {
} }
break; break;
case 1://has been parallel to the mark case 1://has been parallel to the mark
if (boat.isStarboardSide(roundingMark) && if (boat.isStarboardSide(roundingData.getMarkToRound()) &&
GPSCoordinate.passesLine(roundingMark.getPosition(), GPSCoordinate.passesLine(
roundingChecks.get(1), boat.getPosition(), Bearing.fromDegrees(legBearing.degrees() + 90)) && //positive 90 from bearing because of starboard rounding roundingData.getMarkToRound().getPosition(),
boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(1)))) { roundingData.getRoundCheck2(),
boat.getPosition(),
Bearing.fromDegrees(
GPSCoordinate.calculateBearing(
roundingData.getMarkToRound().getPosition(),
roundingData.getRoundCheck2() ).degrees() + 90)) && //positive 90 from bearing because of starboard rounding
boat.isBetweenGate(
roundingData.getMarkToRound(),
Mark.tempMark(roundingData.getRoundCheck2())) ) {
boat.increaseRoundingStatus(); boat.increaseRoundingStatus();
} }
break; break;
@ -574,67 +594,29 @@ public class MockRace extends RaceState {
* @param timeElapsed The total time, in milliseconds, that has elapsed since the race started. * @param timeElapsed The total time, in milliseconds, that has elapsed since the race started.
*/ */
protected void checkPosition(MockBoat boat, long timeElapsed) { protected void checkPosition(MockBoat boat, long timeElapsed) {
//The distance, in nautical miles, within which the boat needs to get in order to consider that it has reached the marker.
double epsilonNauticalMiles = boat.getCurrentLeg().getEndCompoundMark().getRoundingDistance(); //250 meters.
if (boat.calculateDistanceToNextMarker() < epsilonNauticalMiles) {
//Boat is within an acceptable distance from the mark.
GPSCoordinate startDirectionLinePoint = boat.getCurrentLeg().getStartCompoundMark().getMark1Position();
GPSCoordinate endDirectionLinePoint = boat.getCurrentLeg().getEndCompoundMark().getMark1Position();
Bearing bearingOfDirectionLine = GPSCoordinate.calculateBearing(startDirectionLinePoint, endDirectionLinePoint);
//use the direction line to create three invisible points that act as crossover lines a boat must cross
//to round a mark
double bearingToAdd;
if (boat.getCurrentLeg().getEndCompoundMark().getRoundingType() == RoundingType.Port ||
boat.getCurrentLeg().getEndCompoundMark().getRoundingType() == RoundingType.SP){
bearingToAdd = 90;
}else{
bearingToAdd = -90;
}
GPSCoordinate roundCheck1 = GPSCoordinate.calculateNewPosition(endDirectionLinePoint,
epsilonNauticalMiles, Azimuth.fromDegrees(bearingOfDirectionLine.degrees() + bearingToAdd));
GPSCoordinate roundCheck2;
try{
Leg nextLeg = getLegs().get(getLegs().indexOf(boat.getCurrentLeg()) + 1);
GPSCoordinate startNextDirectionLinePoint = nextLeg.getStartCompoundMark().getMark1Position();
GPSCoordinate endNextDirectionLinePoint = nextLeg.getEndCompoundMark().getMark1Position();
Bearing bearingOfNextDirectionLine = GPSCoordinate.calculateBearing(startNextDirectionLinePoint, endNextDirectionLinePoint);
roundCheck2 = GPSCoordinate.calculateNewPosition(endDirectionLinePoint,
epsilonNauticalMiles, Azimuth.fromDegrees(bearingOfNextDirectionLine.degrees() + bearingToAdd));
}catch(NullPointerException e){
//this is caused by the last leg not being having a leg after it
roundCheck2 = roundCheck1;
}
List<GPSCoordinate> roundingChecks = new ArrayList<GPSCoordinate>(Arrays.asList(roundCheck1, roundCheck2));
switch (boat.getCurrentLeg().getEndCompoundMark().getRoundingType()) {
case SP://Not yet implemented so these gates will be rounded port side
case Port:
boatRoundingCheckPort(boat, roundingChecks, bearingOfDirectionLine);
break;
case PS://not yet implemented so these gates will be rounded starboard side
case Starboard:
boatRoundingCheckStarboard(boat, roundingChecks, bearingOfDirectionLine);
break;
}
switch (boat.getCurrentLeg().getEndCompoundMark().getRoundingType()) {
case SP://Not yet implemented so these gates will be rounded port side
case Port:
boatRoundingCheckPort(
boat,
getMarkRoundingSequence().getRoundingData(boat.getCurrentLeg()) );
break;
case PS://not yet implemented so these gates will be rounded starboard side
case Starboard:
boatRoundingCheckStarboard(
boat,
getMarkRoundingSequence().getRoundingData(boat.getCurrentLeg()) );
break;
}
//Check if the boat has finished or stopped racing.
if (this.isLastLeg(boat.getCurrentLeg())) {
//Boat has finished.
boat.setTimeFinished(timeElapsed);
boat.setCurrentSpeed(0);
boat.setStatus(BoatStatusEnum.FINISHED);
} //Check if the boat has finished or stopped racing.
if (this.isLastLeg(boat.getCurrentLeg())) {
//Boat has finished.
boat.setTimeFinished(timeElapsed);
boat.setCurrentSpeed(0);
boat.setStatus(BoatStatusEnum.FINISHED);
} }

@ -38,7 +38,7 @@ public class SourceIdAllocator {
if (!((mockRace.getRaceStatusEnum() == RaceStatusEnum.PRESTART) if (!((mockRace.getRaceStatusEnum() == RaceStatusEnum.PRESTART)
|| (mockRace.getRaceStatusEnum() == RaceStatusEnum.WARNING))) { || (mockRace.getRaceStatusEnum() == RaceStatusEnum.WARNING))) {
throw new SourceIDAllocationException("Could not allocate a source ID. Can only allocate during pre-start period. It is currently: " + mockRace.getRaceStatusEnum()); throw new SourceIDAllocationException("Could not allocate a source ID. Can only allocate during pre-start or warning period. It is currently: " + mockRace.getRaceStatusEnum());
} }
List<Integer> allocatedIDs = mockRace.getRaceDataSource().getParticipants(); List<Integer> allocatedIDs = mockRace.getRaceDataSource().getParticipants();

@ -17,6 +17,7 @@ import shared.xml.XMLUtilities;
import javax.xml.bind.JAXBException; import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
@ -52,37 +53,52 @@ public class RaceXMLCreator {
/** /**
* Rotates the race in a specified direction. * Rotates the race in a specified direction.
* @param s xml file name * @param s xml file name or contents.
* @param fileType Whether s is a file name or contents.
* @param degrees degrees to rotate * @param degrees degrees to rotate
* @param tutorial Whether we wish to run the tutorial - this changes the race start time.
* @return the new xml file as a string * @return the new xml file as a string
* @throws XMLReaderException if the xml is not readable * @throws XMLReaderException if the xml is not readable
* @throws InvalidRaceDataException if the race is invalid * @throws InvalidRaceDataException if the race is invalid
* @throws JAXBException if the Race class cannot be parsed into a xml.
* @throws IOException if the schema file cannot be found
* @throws SAXException error in schema file
* @throws ParserConfigurationException error in parsing the schema file
*/ */
public static String alterRaceToWind(String s, double degrees, boolean tutorial) throws XMLReaderException, InvalidRaceDataException, JAXBException, IOException, SAXException, ParserConfigurationException { public static String alterRaceToWind(String s, XMLFileType fileType, double degrees, boolean tutorial) throws XMLReaderException, InvalidRaceDataException {
RaceXMLReader reader = new RaceXMLReader(s, XMLFileType.ResourcePath);
RaceXMLReader reader = new RaceXMLReader(s, fileType);
XMLRace race = XMLUtilities.xmlToClass(
RaceXMLCreator.class.getClassLoader().getResourceAsStream(s), try {
RaceXMLCreator.class.getClassLoader().getResource("mock/mockXML/schema/raceSchema.xsd"),
XMLRace.class); XMLRace race = XMLUtilities.xmlToClass(
s,
if(tutorial){ RaceXMLCreator.class.getClassLoader().getResource("mock/mockXML/schema/raceSchema.xsd"),
setRaceXMLAtCurrentTimeToNow(race, 1000l, 5000l); XMLRace.class);
} else {
setRaceXMLAtCurrentTimeToNow(race); if(tutorial){
} setRaceXMLAtCurrentTimeToNow(race, 1000l, 5000l);
} else {
setRaceXMLAtCurrentTimeToNow(race);
}
double raceOriginalBearing = getLineAngle(getLeewardGate(reader).getMark1Position(), getWindwardGate(reader).getMark1Position());
double degreesToRotate = degrees - raceOriginalBearing; CompoundMark leewardGate = getLeewardGate(reader);
CompoundMark windwardGate = getWindwardGate(reader);
alterRaceRotation(race, degreesToRotate); double raceOriginalBearing = 0;
return XMLUtilities.classToXML(race); /*if (leewardGate != null && windwardGate != null) {
raceOriginalBearing = getLineAngle(
leewardGate.getMark1Position(),
windwardGate.getMark1Position() );
}*/
double degreesToRotate = degrees - raceOriginalBearing;
alterRaceRotation(race, degreesToRotate);
return XMLUtilities.classToXML(race);
} catch (ParserConfigurationException | IOException | SAXException | JAXBException e) {
throw new InvalidRaceDataException("Could not parse or marshall race data file.", e);
}
} }
@ -179,8 +195,6 @@ public class RaceXMLCreator {
//The start time is current time + 4 minutes. prestart is 3 minutes, and we add another minute. //The start time is current time + 4 minutes. prestart is 3 minutes, and we add another minute.
long millisecondsToAdd = racePrestartTime + racePreparatoryTime; long millisecondsToAdd = racePrestartTime + racePreparatoryTime;
long secondsToAdd = millisecondsToAdd / 1000; long secondsToAdd = millisecondsToAdd / 1000;
//Scale the time using our time scalar.
secondsToAdd = secondsToAdd / Constants.RaceTimeScale;
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ"); DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
ZonedDateTime creationTime = ZonedDateTime.now(); ZonedDateTime creationTime = ZonedDateTime.now();

@ -187,7 +187,7 @@ public class CompoundMark extends XMLCompoundMark{
} }
//finds the mark furthest west and east //finds the mark furthest west and east
if(this.getMark1Position().getLongitude() > this.getMark2Position().getLongitude()){ if(this.getMark1Position().getLongitude() < this.getMark2Position().getLongitude()){
westMostMark = this.mark1; westMostMark = this.mark1;
eastMostMark = this.mark2; eastMostMark = this.mark2;
}else{ }else{

@ -28,18 +28,19 @@ public class Constants {
* Frame periods are multiplied by this to get the amount of time a single frame represents. * Frame periods are multiplied by this to get the amount of time a single frame represents.
* E.g., frame period = 20ms, scale = 5, frame represents 20 * 5 = 100ms, and so boats are simulated for 100ms, even though only 20ms actually occurred. * E.g., frame period = 20ms, scale = 5, frame represents 20 * 5 = 100ms, and so boats are simulated for 100ms, even though only 20ms actually occurred.
*/ */
public static final int RaceTimeScale = 2;//10; public static final int RaceTimeScale = 10;
/** /**
* The race pre-start time, in milliseconds. 3 minutes. * The race pre-start time, in milliseconds. 30 seconds.
* Official time is 3 minutes.
*/ */
public static final long RacePreStartTime = 3 * 60 * 1000; public static final long RacePreStartTime = 30 * 1000;
/** /**
* The race preparatory time, in milliseconds. 1 minute. * The race preparatory time, in milliseconds. 10 seconds.
* Official time is 1 minute.
*/ */
public static final long RacePreparatoryTime = 1 * 60 * 1000; public static final long RacePreparatoryTime = 10 * 1000;
/** /**
* The number of milliseconds in one hour. * The number of milliseconds in one hour.
* <br> * <br>

@ -0,0 +1,118 @@
package shared.model;
/**
* Contains data related to mark rounding for a specific leg.
*/
public class MarkRoundingData {
/**
* The leg this relates to.
*/
private Leg leg;
/**
* The mark that should be rounded.
*/
private Mark markToRound;
/**
* The bearing of the leg.
*/
private Bearing legBearing;
/**
* The bearing of the next leg.
*/
private Bearing nextLegBearing;
/**
* The location of the first rounding check point.
*/
private GPSCoordinate roundCheck1;
/**
* The location of the second rounding check point.
*/
private GPSCoordinate roundCheck2;
/**
* A halfway point between mark to round and roundCheck1.
*/
private GPSCoordinate roundCheck1Halfway;
/**
* A halfway point between mark to round and roundCheck2.
*/
private GPSCoordinate roundCheck2Halfway;
public MarkRoundingData() {
}
public Leg getLeg() {
return leg;
}
public void setLeg(Leg leg) {
this.leg = leg;
}
public Mark getMarkToRound() {
return markToRound;
}
public void setMarkToRound(Mark markToRound) {
this.markToRound = markToRound;
}
public Bearing getLegBearing() {
return legBearing;
}
public void setLegBearing(Bearing legBearing) {
this.legBearing = legBearing;
}
public Bearing getNextLegBearing() {
return nextLegBearing;
}
public void setNextLegBearing(Bearing nextLegBearing) {
this.nextLegBearing = nextLegBearing;
}
public GPSCoordinate getRoundCheck1() {
return roundCheck1;
}
public void setRoundCheck1(GPSCoordinate roundCheck1) {
this.roundCheck1 = roundCheck1;
}
public GPSCoordinate getRoundCheck2() {
return roundCheck2;
}
public void setRoundCheck2(GPSCoordinate roundCheck2) {
this.roundCheck2 = roundCheck2;
}
public GPSCoordinate getRoundCheck1Halfway() {
return roundCheck1Halfway;
}
public void setRoundCheck1Halfway(GPSCoordinate roundCheck1Halfway) {
this.roundCheck1Halfway = roundCheck1Halfway;
}
public GPSCoordinate getRoundCheck2Halfway() {
return roundCheck2Halfway;
}
public void setRoundCheck2Halfway(GPSCoordinate roundCheck2Halfway) {
this.roundCheck2Halfway = roundCheck2Halfway;
}
}

@ -0,0 +1,210 @@
package shared.model;
import java.util.*;
import static shared.enums.RoundingType.*;
/**
* This class contains a sequence of points that describe the mark rounding order for a course.
*/
public class MarkRoundingSequence {
/**
* Legs in the race.
*/
private List<Leg> legs;
/**
* For each leg, mark rounding information.
*/
private Map<Leg, MarkRoundingData> roundingPoints;
public MarkRoundingSequence(List<Leg> legs) {
this.legs = legs;
generateRoundingPoints();
}
/**
* Returns the rounding points for a given leg.
* @param leg Leg to check.
* @return Rounding points for leg.
*/
public MarkRoundingData getRoundingData(Leg leg) {
return roundingPoints.get(leg);
}
/**
* Generates the rounding points for all legs in the race.
*/
private void generateRoundingPoints() {
this.roundingPoints = new HashMap<>(this.legs.size());
for (int i = 0; i < this.legs.size(); i++) {
Leg currentLeg = this.legs.get(i);
Optional<Leg> nextLeg = Optional.empty();
if (i < legs.size() - 1) {
nextLeg = Optional.of(this.legs.get(i + 1));
}
generateRoundingPoint(currentLeg, nextLeg);
}
}
/**
* Generates the rounding points for a specific leg.
* @param currentLeg The leg to generate rounding points for.
* @param nextLeg The following leg, used to help generate rounding points. Final leg of race doesn't have a following leg.
*/
private void generateRoundingPoint(Leg currentLeg, Optional<Leg> nextLeg) {
Bearing bearingToAddFirstPoint = calculateBearingToAdd(currentLeg);
GPSCoordinate startCoord = currentLeg.getStartCompoundMark().getAverageGPSCoordinate();
GPSCoordinate endCoord = currentLeg.getEndCompoundMark().getAverageGPSCoordinate();
Bearing legBearing = GPSCoordinate.calculateBearing(startCoord, endCoord);
Bearing nextBearing = legBearing;
Mark markToRound = currentLeg.getEndCompoundMark().getMarkForRounding(legBearing);
GPSCoordinate roundCheck1;
if (currentLeg.getEndCompoundMark().getMark2() == null) {
//End is a single mark.
roundCheck1 = calculateRoundingCheckPoint(
currentLeg,
markToRound,
legBearing,
bearingToAddFirstPoint);
} else {
//End is a gate.
if (markToRound == currentLeg.getEndCompoundMark().getMark1()) {
roundCheck1 = currentLeg.getEndCompoundMark().getMark2().getPosition();
} else {
roundCheck1 = currentLeg.getEndCompoundMark().getMark1().getPosition();
}
}
//TODO the halfway points currently haven't been done properly.
GPSCoordinate roundCheck1Halfway = calculateRoundingCheckPoint(
currentLeg,
markToRound,
legBearing,
bearingToAddFirstPoint);
GPSCoordinate roundCheck2 = roundCheck1;
GPSCoordinate roundCheck2Halfway = roundCheck1Halfway;
if (nextLeg.isPresent()) {
Bearing bearingToAddSecondPoint = bearingToAddFirstPoint;//calculateBearingToAdd(nextLeg.get());
GPSCoordinate startCoord2 = nextLeg.get().getStartCompoundMark().getAverageGPSCoordinate();
GPSCoordinate endCoord2 = nextLeg.get().getEndCompoundMark().getAverageGPSCoordinate();
nextBearing = GPSCoordinate.calculateBearing(startCoord2, endCoord2);
roundCheck2 = calculateRoundingCheckPoint(
currentLeg,
markToRound,
nextBearing,
bearingToAddSecondPoint);
roundCheck2Halfway = calculateRoundingCheckPoint(
currentLeg,
markToRound,
nextBearing,
bearingToAddSecondPoint);
}
MarkRoundingData roundingData = new MarkRoundingData();
roundingData.setLeg(currentLeg);
roundingData.setLegBearing(legBearing);
roundingData.setNextLegBearing(nextBearing);
roundingData.setMarkToRound(markToRound);
roundingData.setRoundCheck1(roundCheck1);
roundingData.setRoundCheck1Halfway(roundCheck1Halfway);
roundingData.setRoundCheck2(roundCheck2);
roundingData.setRoundCheck2Halfway(roundCheck2Halfway);
this.roundingPoints.put(currentLeg, roundingData);
//Rounding points:
//each mark/gate has a specific mark to round. Call this ROUNDINGMARK
// with a mark, it is the mark
// with a gate, it depends if it is a starboard or port gate.
// it is the mark that allows the boat to enter between both marks of the gate, whilst obeying the starboard/port requirement.
//let the bearing between start of leg and end of leg be called LEGBEARING
//the first rounding point is ROUNDINGDISTANCE units away from the ROUNDINGMARK, on an angle perpendicular to LEGBEARING.
// the angle means that the rounding mark is at the center of a gate, for gates.
//the second rounding point is the same as the first, except LEGBEARING is the bearing between end of current leg, and start of next leg.
}
/**
* Calculates the location of the rounding check point, which together with the mark to round, forms a line that the boat must cross to round the mark.
* @param leg Leg of race to check.
* @param markToRound Mark at end of leg to round.
* @param legBearing The bearing of the nearest leg. For the first rounding point this is the leg's bearing, for the second rounding point it is the next leg's bearing.
* @param bearingToAdd The bearing to add to the leg bearing to get a perpendicular bearing.
* @return The location of the rounding point, which together with the mark to round forms a line the boat must cross.
*/
private GPSCoordinate calculateRoundingCheckPoint(Leg leg, Mark markToRound, Bearing legBearing, Bearing bearingToAdd) {
double roundingDistanceMeters = leg.getEndCompoundMark().getRoundingDistance();
//We project from rounding mark to get the second point which forms the line the boat must cross.
/*
c2
|
|
r------c1
b
*/
GPSCoordinate roundCheck = GPSCoordinate.calculateNewPosition(
markToRound.getPosition(),
roundingDistanceMeters,
Azimuth.fromDegrees(legBearing.degrees() + bearingToAdd.degrees()) );
return roundCheck;
}
/**
* Calculates the bearing that must be added to a leg's bearing to calculate a perpendicular bearing, used for finding rounding points.
* @param leg Leg to check.
* @return Bearing to add. Will be either +90 or -90.
*/
private Bearing calculateBearingToAdd(Leg leg) {
if (leg.getEndCompoundMark().getRoundingType() == Port ||
leg.getEndCompoundMark().getRoundingType() == SP) {
return Bearing.fromDegrees(90);
} else {
return Bearing.fromDegrees(-90);
}
}
}

@ -47,6 +47,12 @@ public abstract class RaceState extends Observable{
private ObservableList<Leg> legs; private ObservableList<Leg> legs;
/**
* The sequence of rounding points for each leg/mark.
*/
private MarkRoundingSequence markRoundingSequence;
/** /**
* The clock which tracks the race's start time, current time, and elapsed duration. * The clock which tracks the race's start time, current time, and elapsed duration.
@ -103,10 +109,15 @@ public abstract class RaceState extends Observable{
*/ */
protected void useLegsList(List<Leg> legs) { protected void useLegsList(List<Leg> legs) {
this.legs.setAll(legs); this.legs.setAll(legs);
//We create this before adding the extra finish leg, as it doesn't contain compound marks.
this.markRoundingSequence = new MarkRoundingSequence(getLegs());
//We add a "dummy" leg at the end of the race. //We add a "dummy" leg at the end of the race.
if (getLegs().size() > 0) { if (getLegs().size() > 0) {
getLegs().add(new Leg("Finish", getLegs().size())); getLegs().add(new Leg("Finish", getLegs().size()));
} }
} }
@ -368,6 +379,11 @@ public abstract class RaceState extends Observable{
} }
/**
* Returns the rounding sequences for each leg.
* @return Rounding sequence for each leg.
*/
public MarkRoundingSequence getMarkRoundingSequence() {
return markRoundingSequence;
}
} }

@ -50,7 +50,7 @@ import javax.xml.bind.annotation.XmlType;
}) })
public class XMLParticipants { public class XMLParticipants {
@XmlElement(name = "Yacht", required = true) @XmlElement(name = "Yacht", required = false)
protected List<XMLYacht> yacht; protected List<XMLYacht> yacht;
/** /**

@ -49,7 +49,7 @@ public class XMLUtilities {
return xmlToClass(document, schemaURL, c); return xmlToClass(document, schemaURL, c);
} }
public static Object xmlToClass(String xml, URL schemaURL, Class c) throws ParserConfigurationException, IOException, SAXException, JAXBException { public static <T> T xmlToClass(String xml, URL schemaURL, Class<T> c) throws ParserConfigurationException, IOException, SAXException, JAXBException {
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = parser.parse(new InputSource(new ByteArrayInputStream(xml.getBytes("utf-8")))); Document document = parser.parse(new InputSource(new ByteArrayInputStream(xml.getBytes("utf-8"))));
@ -63,10 +63,10 @@ public class XMLUtilities {
* @param c The XML class to convert to. * @param c The XML class to convert to.
* @param <T> The XML class to convert to. * @param <T> The XML class to convert to.
* @return The XML class object. * @return The XML class object.
* @throws ParserConfigurationException * @throws ParserConfigurationException Thrown if input cannot be converted to class.
* @throws IOException * @throws IOException Thrown if input cannot be converted to class.
* @throws SAXException * @throws SAXException Thrown if input cannot be converted to class.
* @throws JAXBException * @throws JAXBException Thrown if input cannot be converted to class.
*/ */
public static <T> T xmlToClass(InputStream i, URL schemaURL, Class<T> c) throws ParserConfigurationException, IOException, SAXException, JAXBException { public static <T> T xmlToClass(InputStream i, URL schemaURL, Class<T> c) throws ParserConfigurationException, IOException, SAXException, JAXBException {
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();

@ -184,7 +184,7 @@ public class BoatConfig {
* Objects of the following type(s) are allowed in the list * Objects of the following type(s) are allowed in the list
* {@link BoatConfig.Boats.Boat } * {@link BoatConfig.Boats.Boat }
* *
* * @return List of Boat entries.
*/ */
public List<BoatConfig.Boats.Boat> getBoat() { public List<BoatConfig.Boats.Boat> getBoat() {
if (boat == null) { if (boat == null) {
@ -327,7 +327,7 @@ public class BoatConfig {
/** /**
* Gets the value of the sourceID property. * Gets the value of the sourceID property.
* * @return source id.
*/ */
public int getSourceID() { public int getSourceID() {
return sourceID; return sourceID;
@ -335,7 +335,7 @@ public class BoatConfig {
/** /**
* Sets the value of the sourceID property. * Sets the value of the sourceID property.
* * @param value new source id.
*/ */
public void setSourceID(int value) { public void setSourceID(int value) {
this.sourceID = value; this.sourceID = value;
@ -494,7 +494,7 @@ public class BoatConfig {
/** /**
* Gets the value of the y property. * Gets the value of the y property.
* * @return Y value.
*/ */
public double getY() { public double getY() {
return y; return y;
@ -502,7 +502,7 @@ public class BoatConfig {
/** /**
* Sets the value of the y property. * Sets the value of the y property.
* * @param value new y value.
*/ */
public void setY(double value) { public void setY(double value) {
this.y = value; this.y = value;
@ -510,7 +510,7 @@ public class BoatConfig {
/** /**
* Gets the value of the z property. * Gets the value of the z property.
* * @return z value.
*/ */
public double getZ() { public double getZ() {
return z; return z;
@ -518,7 +518,7 @@ public class BoatConfig {
/** /**
* Sets the value of the z property. * Sets the value of the z property.
* * @param value new z value.
*/ */
public void setZ(double value) { public void setZ(double value) {
this.z = value; this.z = value;

@ -38,7 +38,7 @@ public class ObjectFactory {
/** /**
* Create an instance of {@link BoatConfig } * Create an instance of {@link BoatConfig }
* * @return BoatConfig.
*/ */
public BoatConfig createBoatConfig() { public BoatConfig createBoatConfig() {
return new BoatConfig(); return new BoatConfig();
@ -46,7 +46,7 @@ public class ObjectFactory {
/** /**
* Create an instance of {@link BoatConfig.Boats } * Create an instance of {@link BoatConfig.Boats }
* * @return Boats.
*/ */
public BoatConfig.Boats createBoatConfigBoats() { public BoatConfig.Boats createBoatConfigBoats() {
return new BoatConfig.Boats(); return new BoatConfig.Boats();
@ -54,7 +54,7 @@ public class ObjectFactory {
/** /**
* Create an instance of {@link BoatConfig.Boats.Boat } * Create an instance of {@link BoatConfig.Boats.Boat }
* * @return Boat.
*/ */
public BoatConfig.Boats.Boat createBoatConfigBoatsBoat() { public BoatConfig.Boats.Boat createBoatConfigBoatsBoat() {
return new BoatConfig.Boats.Boat(); return new BoatConfig.Boats.Boat();
@ -62,7 +62,7 @@ public class ObjectFactory {
/** /**
* Create an instance of {@link BoatConfig.Boats.Boat.GPSposition } * Create an instance of {@link BoatConfig.Boats.Boat.GPSposition }
* * @return GPSposition.
*/ */
public BoatConfig.Boats.Boat.GPSposition createBoatConfigBoatsBoatGPSposition() { public BoatConfig.Boats.Boat.GPSposition createBoatConfigBoatsBoatGPSposition() {
return new BoatConfig.Boats.Boat.GPSposition(); return new BoatConfig.Boats.Boat.GPSposition();

@ -38,7 +38,7 @@ public class ObjectFactory {
/** /**
* Create an instance of {@link RegattaConfig } * Create an instance of {@link RegattaConfig }
* * @return RegattaConfig.
*/ */
public RegattaConfig createRegattaConfig() { public RegattaConfig createRegattaConfig() {
return new RegattaConfig(); return new RegattaConfig();

@ -74,7 +74,7 @@ public class RegattaConfig {
/** /**
* Gets the value of the regattaID property. * Gets the value of the regattaID property.
* * @return regatta id.
*/ */
public int getRegattaID() { public int getRegattaID() {
return regattaID; return regattaID;
@ -82,7 +82,7 @@ public class RegattaConfig {
/** /**
* Sets the value of the regattaID property. * Sets the value of the regattaID property.
* * @param value new regatta id.
*/ */
public void setRegattaID(int value) { public void setRegattaID(int value) {
this.regattaID = value; this.regattaID = value;
@ -138,7 +138,7 @@ public class RegattaConfig {
/** /**
* Gets the value of the centralLatitude property. * Gets the value of the centralLatitude property.
* * @return central latitude.
*/ */
public double getCentralLatitude() { public double getCentralLatitude() {
return centralLatitude; return centralLatitude;
@ -146,7 +146,7 @@ public class RegattaConfig {
/** /**
* Sets the value of the centralLatitude property. * Sets the value of the centralLatitude property.
* * @param value new central latitude.
*/ */
public void setCentralLatitude(double value) { public void setCentralLatitude(double value) {
this.centralLatitude = value; this.centralLatitude = value;
@ -154,7 +154,7 @@ public class RegattaConfig {
/** /**
* Gets the value of the centralLongitude property. * Gets the value of the centralLongitude property.
* * @return central longitude.
*/ */
public double getCentralLongitude() { public double getCentralLongitude() {
return centralLongitude; return centralLongitude;
@ -162,7 +162,7 @@ public class RegattaConfig {
/** /**
* Sets the value of the centralLongitude property. * Sets the value of the centralLongitude property.
* * @param value new central longitude.
*/ */
public void setCentralLongitude(double value) { public void setCentralLongitude(double value) {
this.centralLongitude = value; this.centralLongitude = value;
@ -170,7 +170,7 @@ public class RegattaConfig {
/** /**
* Gets the value of the centralAltitude property. * Gets the value of the centralAltitude property.
* * @return central altitude.
*/ */
public double getCentralAltitude() { public double getCentralAltitude() {
return centralAltitude; return centralAltitude;
@ -178,7 +178,7 @@ public class RegattaConfig {
/** /**
* Sets the value of the centralAltitude property. * Sets the value of the centralAltitude property.
* * @param value new central altitude.
*/ */
public void setCentralAltitude(double value) { public void setCentralAltitude(double value) {
this.centralAltitude = value; this.centralAltitude = value;
@ -186,7 +186,7 @@ public class RegattaConfig {
/** /**
* Gets the value of the utcOffset property. * Gets the value of the utcOffset property.
* * @return utc offset.
*/ */
public double getUtcOffset() { public double getUtcOffset() {
return utcOffset; return utcOffset;
@ -194,7 +194,7 @@ public class RegattaConfig {
/** /**
* Sets the value of the utcOffset property. * Sets the value of the utcOffset property.
* * @param value new utc offset.
*/ */
public void setUtcOffset(double value) { public void setUtcOffset(double value) {
this.utcOffset = value; this.utcOffset = value;
@ -202,7 +202,7 @@ public class RegattaConfig {
/** /**
* Gets the value of the magneticVariation property. * Gets the value of the magneticVariation property.
* * @return magnetic variation.
*/ */
public double getMagneticVariation() { public double getMagneticVariation() {
return magneticVariation; return magneticVariation;
@ -210,7 +210,7 @@ public class RegattaConfig {
/** /**
* Sets the value of the magneticVariation property. * Sets the value of the magneticVariation property.
* * @param value new magnetic variation.
*/ */
public void setMagneticVariation(double value) { public void setMagneticVariation(double value) {
this.magneticVariation = value; this.magneticVariation = value;

@ -325,18 +325,6 @@ public class ResizableRaceCanvas extends ResizableCanvas {
} }
/*
//If the race hasn't started, we set the time since last mark to the current time, to ensure we don't start counting until the race actually starts.
if ((boat.getStatus() != BoatStatusEnum.RACING) && (boat.getStatus() == BoatStatusEnum.FINISHED)) {
boat.setTimeAtLastMark(visualiserRace.getVisualiserRaceState().getRaceClock().getCurrentTime());
}
*/
} }
/** /**
@ -504,6 +492,52 @@ public class ResizableRaceCanvas extends ResizableCanvas {
} }
private void drawRoundingLines() {
//ugly hack
//rounding lines
//Boat is within an acceptable distance from the mark.
VisualiserBoat boat = null;
for (VisualiserBoat visualiserBoat : new ArrayList<>(raceState.getBoats())) {
if (visualiserBoat.isClientBoat()) {
boat = visualiserBoat;
}
}
if (boat == null) {
return;
}
Leg currentLeg = boat.getCurrentLeg();
MarkRoundingData roundingData = raceState.getMarkRoundingSequence().getRoundingData(currentLeg);
//To screen coords.
GraphCoordinate legEnd = map.convertGPS(roundingData.getMarkToRound().getPosition());
GraphCoordinate round1 = map.convertGPS(roundingData.getRoundCheck1());
GraphCoordinate round2 = map.convertGPS(roundingData.getRoundCheck2());
gc.strokeLine(
legEnd.getX(),
legEnd.getY(),
round1.getX(),
round1.getY() );
gc.strokeLine(
legEnd.getX(),
legEnd.getY(),
round2.getX(),
round2.getY() );
drawCircle(round1, 12, Color.ORANGE);
drawCircle(round2, 12, Color.GREEN);
}
/** /**
* Draws all of the {@link Mark}s on the canvas. * Draws all of the {@link Mark}s on the canvas.
@ -513,6 +547,7 @@ public class ResizableRaceCanvas extends ResizableCanvas {
for (Mark mark : new ArrayList<>(raceState.getMarks())) { for (Mark mark : new ArrayList<>(raceState.getMarks())) {
drawMark(mark); drawMark(mark);
} }
} }
@ -621,6 +656,9 @@ public class ResizableRaceCanvas extends ResizableCanvas {
//Marks. //Marks.
drawMarks(); drawMarks();
//TEMP
drawRoundingLines();
} }
/** /**
@ -655,7 +693,7 @@ public class ResizableRaceCanvas extends ResizableCanvas {
//finds the direction of the current leg as a bearing //finds the direction of the current leg as a bearing
startDirectionLinePoint = legStartPoint; startDirectionLinePoint = legStartPoint;
GPSCoordinate tempEndDirectionLinePoint = legs.get(index).getEndCompoundMark().getAverageGPSCoordinate(); GPSCoordinate tempEndDirectionLinePoint = legs.get(index).getEndCompoundMark().getMark1Position();
bearingOfDirectionLine = GPSCoordinate.calculateBearing(startDirectionLinePoint, tempEndDirectionLinePoint); bearingOfDirectionLine = GPSCoordinate.calculateBearing(startDirectionLinePoint, tempEndDirectionLinePoint);

@ -15,7 +15,7 @@
<xs:element name="Participants"> <xs:element name="Participants">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
<xs:element name="Yacht" maxOccurs="unbounded"> <xs:element name="Yacht" maxOccurs="unbounded" minOccurs="0">
<xs:complexType> <xs:complexType>
<xs:attribute name="SourceID" type="xs:int" use="required"/> <xs:attribute name="SourceID" type="xs:int" use="required"/>
<xs:attribute name="Entry" type="xs:string"/> <xs:attribute name="Entry" type="xs:string"/>

@ -1,5 +1,6 @@
package shared.dataInput; package shared.dataInput;
import mock.app.Event;
import shared.enums.XMLFileType; import shared.enums.XMLFileType;
import shared.exceptions.InvalidRaceDataException; import shared.exceptions.InvalidRaceDataException;
import shared.exceptions.XMLReaderException; import shared.exceptions.XMLReaderException;
@ -16,7 +17,7 @@ public class RaceXMLReaderTest {
String raceXMLString = null; String raceXMLString = null;
try { try {
raceXMLString = XMLReader.readXMLFileToString("mock/mockXML/raceTest.xml", StandardCharsets.UTF_8); raceXMLString = Event.setRaceXMLAtCurrentTimeToNow(XMLReader.readXMLFileToString("mock/mockXML/raceTest.xml", StandardCharsets.UTF_8));
} catch (XMLReaderException e) { } catch (XMLReaderException e) {
throw new InvalidRaceDataException("Could not read race XML file into a string.", e); throw new InvalidRaceDataException("Could not read race XML file into a string.", e);

@ -2,17 +2,18 @@
<Race> <Race>
<RaceID>5326</RaceID> <RaceID>5326</RaceID>
<RaceType>FLEET</RaceType> <RaceType>FLEET</RaceType>
<CreationTimeDate>2017-04-19T15:30:00+1200</CreationTimeDate> <CreationTimeDate>RACE_CREATION_TIME</CreationTimeDate>
<RaceStartTime Postpone="false" Time="2017-04-19T15:33:00+1200"/> <RaceStartTime Postpone="false" Time="RACE_START_TIME"/>
<Participants> <Participants>
</Participants> </Participants>
<CompoundMarkSequence> <CompoundMarkSequence>
<Corner CompoundMarkID="1" SeqID="1"/> <Corner SeqID="1" CompoundMarkID="1" Rounding="SP" ZoneSize="3" />
<Corner CompoundMarkID="2" SeqID="2"/> <Corner SeqID="2" CompoundMarkID="2" Rounding="Port" ZoneSize="3" />
<Corner CompoundMarkID="4" SeqID="3"/> <Corner SeqID="3" CompoundMarkID="4" Rounding="Port" ZoneSize="3" />
<Corner CompoundMarkID="3" SeqID="4"/> <Corner SeqID="4" CompoundMarkID="3" Rounding="Starboard" ZoneSize="3" />
<Corner CompoundMarkID="4" SeqID="5"/> <Corner SeqID="5" CompoundMarkID="4" Rounding="Port" ZoneSize="3" />
<Corner CompoundMarkID="5" SeqID="6"/> <Corner SeqID="6" CompoundMarkID="5" Rounding="SP" ZoneSize="3"/>
</CompoundMarkSequence> </CompoundMarkSequence>
<Course> <Course>
<CompoundMark CompoundMarkID="1" Name="Start Line"> <CompoundMark CompoundMarkID="1" Name="Start Line">

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_121" class="java.beans.XMLDecoder">
<object class="java.util.HashMap">
<void method="put">
<string>SPACE</string>
<object class="visualiser.gameController.Keys.VMGKey"/>
</void>
<void method="put">
<string>SHIFT</string>
<object class="visualiser.gameController.Keys.SailsToggleKey"/>
</void>
<void method="put">
<string>LEFT</string>
<object class="visualiser.gameController.Keys.UpWindKey"/>
</void>
<void method="put">
<string>X</string>
<object class="visualiser.gameController.Keys.ZoomOutKey"/>
</void>
<void method="put">
<string>ENTER</string>
<object class="visualiser.gameController.Keys.TackGybeKey"/>
</void>
<void method="put">
<string>RIGHT</string>
<object class="visualiser.gameController.Keys.DownWindKey"/>
</void>
<void method="put">
<string>Z</string>
<object class="visualiser.gameController.Keys.ZoomInKey"/>
</void>
</object>
</java>
Loading…
Cancel
Save