You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
7.9 KiB

package mock.xml;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import shared.dataInput.RaceXMLReader;
import shared.enums.XMLFileType;
import shared.exceptions.InvalidRaceDataException;
import shared.exceptions.XMLReaderException;
import shared.model.*;
import shared.xml.Race.*;
import shared.xml.XMLUtilities;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.util.JAXBSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.math.BigInteger;
import java.net.URL;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
/**
* Helper Class for creating a Race XML
*/
public class RaceXMLCreator {
/**
* get the windward gate in a race
* @param reader reads in the mark
* @return the windward gate.
*/
public static CompoundMark getWindwardGate(RaceXMLReader reader){
for (CompoundMark mark: reader.getCompoundMarks()){
if (mark.getName().equals("Windward Gate")) return mark;
}
return null;
}
/**
* get the leeward gate in a race
* @param reader reads in the mark
* @return the leeward gate.
*/
public static CompoundMark getLeewardGate(RaceXMLReader reader){
for (CompoundMark mark: reader.getCompoundMarks()){
if (mark.getName().equals("Leeward Gate")) return mark;
}
return null;
}
/**
* Rotates the race in a specified direction.
* @param s xml file name
* @param degrees degrees to rotate
* @return the new xml file as a string
* @throws XMLReaderException if the xml is not readable
* @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) throws XMLReaderException, InvalidRaceDataException, JAXBException, IOException, SAXException, ParserConfigurationException {
RaceXMLReader reader = new RaceXMLReader(s, XMLFileType.ResourcePath);
XMLRace race = (XMLRace) XMLUtilities.xmlToClass(RaceXMLCreator.class.getClassLoader().getResourceAsStream(s),
RaceXMLCreator.class.getClassLoader().getResource("mock/mockXML/schema/raceSchema.xsd"),
XMLRace.class);
setRaceXMLAtCurrentTimeToNow(race);
double raceOriginalBearing = getLineAngle(getLeewardGate(reader).getMark1Position(), getWindwardGate(reader).getMark1Position());
double degreesToRotate = degrees - raceOriginalBearing;
alterRaceRotation(race, degreesToRotate);
JAXBContext context = JAXBContext.newInstance(XMLRace.class);
Marshaller jaxbMarshaller = context.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(race, sw);
return sw.toString();
}
/**
* Rotate the features in a race such as the boundary, and the marks.
* @param race the race to alter
* @param degrees the degrees to rotate by.
*/
public static void alterRaceRotation(XMLRace race, double degrees){
GPSCoordinate center = getCenter(race);
for(XMLLimit limit: race.getCourseLimit().getLimit()){
GPSCoordinate rotatedLim = rotate(center, limitToGPSCoordinate(limit), degrees);
limit.setLat(rotatedLim.getLatitude());
limit.setLon(rotatedLim.getLongitude());
}
for(XMLCompoundMark compoundMark: race.getCourse().getCompoundMark()){
for (XMLMark mark: compoundMark.getMark()){
GPSCoordinate rotatedMark = rotate(center, markToGPSCoordinate(mark), degrees);
mark.setTargetLat(rotatedMark.getLatitude());
mark.setTargetLng(rotatedMark.getLongitude());
}
}
}
/**
* Converts a Race.CourseLimit.Limit to a GPS coordinate
* @param limit limit to convert
* @return gps coordinate corresponding to the limit
*/
public static GPSCoordinate limitToGPSCoordinate(XMLLimit limit){
return new GPSCoordinate(limit.getLat(), limit.getLon());
}
/**
* get new gps coordinate after rotating
* @param pivot center point to rotating from.
* @param point point to rotate
* @param degrees number of degress to rotate by
* @return the new GPSCoordinate of the transformed point.
*/
public static GPSCoordinate rotate(GPSCoordinate pivot, GPSCoordinate point, double degrees){
double radDeg = Math.toRadians(degrees);
double deltaLat = (point.getLatitude() - pivot.getLatitude());
double deltaLon = (point.getLongitude() - pivot.getLongitude());
//map to (0,1) vector and use vector maths to rotate.
double resLat = deltaLat * Math.cos(radDeg) - deltaLon * Math.sin(radDeg) + pivot.getLatitude();
double resLon = deltaLat * Math.sin(radDeg) + deltaLon * Math.cos(radDeg) + pivot.getLongitude();
return new GPSCoordinate(resLat, resLon);
}
/**
* obtains the GPSCoordinates of a mark
* @param mark mark to obtain the GPSCoordinates of
* @return the GPSCOordinatess of a mark
*/
public static GPSCoordinate markToGPSCoordinate(XMLMark mark){
return new GPSCoordinate(mark.getTargetLat(), mark.getTargetLng());
}
/**
* get the center of a race
* @param race race to get the center of
* @return GPSCoordinates of the center
*/
public static GPSCoordinate getCenter(XMLRace race){
double avgLat = 0;
double avgLng = 0;
for (XMLLimit limit: race.getCourseLimit().getLimit()){
avgLat += limit.getLat();
avgLng += limit.getLon();
}
avgLat = avgLat/race.getCourseLimit().getLimit().size();
avgLng = avgLng/race.getCourseLimit().getLimit().size();
return new GPSCoordinate(avgLat, avgLng);
}
/**
* gets the angle of a line
* @param coord1 point a of the line
* @param coord2 point b of the line
* @return the angle in degrees that the bearing of the line is [-180, 180]
*/
public static double getLineAngle(GPSCoordinate coord1, GPSCoordinate coord2){
double dx = coord1.getLongitude() - coord2.getLongitude();
double dy = coord1.getLatitude() - coord2.getLatitude();
return Math.atan2(dy, dx)/Math.PI * 180;
}
/**
* 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.
*/
public static void setRaceXMLAtCurrentTimeToNow(XMLRace raceXML) {
//The start time is current time + 4 minutes. prestart is 3 minutes, and we add another minute.
long millisecondsToAdd = Constants.RacePreStartTime + (1 * 60 * 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");
ZonedDateTime creationTime = ZonedDateTime.now();
raceXML.setCreationTimeDate(dateFormat.format(creationTime));
raceXML.getRaceStartTime().setTime(dateFormat.format(creationTime.plusSeconds(secondsToAdd)));
}
}