Refactored and moved RaceXMLReader to shared/dataInput.

RaceStatus can provide windspeed in knots in addition to mm/sec.
RaceDataSource now provides a list of participating boat sourceIDs instead of boats.
Added a RegattaDataSource interface.
Angle, Azimuth, Bearing and mutable - careful.
Boat has positionInRace.
CompoundMarks have an ID and name.
Marks can be moved (setPosition(...)).

Refactored Mock.Race, Visualiser.StreamedCourse, Visualiser.StreamedRace, into (shared) Race, MockRace, VisualiserRace.

VisualiserBoat has color.
Added xml and polar files into resources folder.
This commit is contained in:
fjc40
2017-07-06 01:30:55 +12:00
parent 7f027c8cc5
commit 8d36d89570
35 changed files with 1806 additions and 1221 deletions
@@ -1,289 +0,0 @@
package mock.dataInput;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import seng302.Exceptions.StreamedCourseXMLException;
import shared.dataInput.BoatDataSource;
import shared.dataInput.RaceDataSource;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.text.ParseException;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
* XML Reader that reads in the race data required for this race
*/
public class RaceXMLReader extends XMLReader implements RaceDataSource {
private static final double COORDINATEPADDING = 0.000;
private GPSCoordinate mapTopLeft, mapBottomRight;
private final List<GPSCoordinate> boundary = new ArrayList<>();
private final Map<Integer,Element> compoundMarkMap = new HashMap<>();
private final Map<Integer, Boat> participants = new HashMap<>();
private final List<Leg> legs = new ArrayList<>();
private final List<CompoundMark> compoundMarks = new ArrayList<>();
private ZonedDateTime creationTimeDate;
private ZonedDateTime raceStartTime;
private int raceID;
private String raceType;
private boolean postpone;
private Map<Integer, Boat> boats;
private Map<Integer, Mark> marks;
/**
* Constructor for Streamed Race XML
* @param filePath path of the file
* @param boatData data for boats in race
* @throws IOException error
* @throws SAXException error
* @throws ParserConfigurationException error
* @throws ParseException error
* @throws StreamedCourseXMLException error
*/
public RaceXMLReader(String filePath, BoatDataSource boatData) throws IOException, SAXException, ParserConfigurationException, ParseException, StreamedCourseXMLException {
this(filePath, boatData, true);
}
/**
* Constructor for Streamed Race XML
* @param filePath file path to read
* @param boatData data of the boats in race
* @param read whether or not to read and store the files straight away.
* @throws IOException error
* @throws SAXException error
* @throws ParserConfigurationException error
* @throws ParseException error
* @throws StreamedCourseXMLException error
*/
public RaceXMLReader(String filePath, BoatDataSource boatData, boolean read) throws IOException, SAXException, ParserConfigurationException, ParseException, StreamedCourseXMLException {
super(filePath);
this.boats = boatData.getBoats();
this.marks = boatData.getMarkerBoats();
if (read) {
read();
}
}
/**
* reads
* @throws StreamedCourseXMLException error
*/
private void read() throws StreamedCourseXMLException {
readRace();
readParticipants();
readCourse();
}
/**
* reads a race
*/
private void readRace() {
DateTimeFormatter dateFormat = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
Element settings = (Element) doc.getElementsByTagName("Race").item(0);
NamedNodeMap raceTimeTag = doc.getElementsByTagName("RaceStartTime").item(0).getAttributes();
if (raceTimeTag.getNamedItem("Time") != null) dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
raceID = Integer.parseInt(getTextValueOfNode(settings, "RaceID"));
raceType = getTextValueOfNode(settings, "RaceType");
creationTimeDate = ZonedDateTime.parse(getTextValueOfNode(settings, "CreationTimeDate"), dateFormat);
if (raceTimeTag.getNamedItem("Time") != null) raceStartTime = ZonedDateTime.parse(raceTimeTag.getNamedItem("Time").getTextContent(), dateFormat);
else raceStartTime = ZonedDateTime.parse(raceTimeTag.getNamedItem("Start").getTextContent(), dateFormat);
postpone = Boolean.parseBoolean(raceTimeTag.getNamedItem("Postpone").getTextContent());
}
/**
* Reads in the participants for htis race
*/
private void readParticipants() {
Element nParticipants = (Element) doc.getElementsByTagName("Participants").item(0);
nParticipants.getChildNodes().getLength();
for (int i = 0; i < nParticipants.getChildNodes().getLength(); i++) {
int sourceID;
Node yacht = nParticipants.getChildNodes().item(i);
if (yacht.getNodeName().equals("Yacht")) {
if (exists(yacht, "SourceID")) {
sourceID = Integer.parseInt(yacht.getAttributes().getNamedItem("SourceID").getTextContent());
participants.put(sourceID, boats.get(sourceID));
}
}
}
}
/**
* reads a course
* @throws StreamedCourseXMLException error
*/
private void readCourse() throws StreamedCourseXMLException {
readCompoundMarks();
readCompoundMarkSequence();
readCourseLimit();
}
/**
* Indexes CompoundMark elements by their ID for use in generating the course, and populates list of Markers.
* @see CompoundMark
*/
private void readCompoundMarks() throws StreamedCourseXMLException {
Element nCourse = (Element) doc.getElementsByTagName("Course").item(0);
for(int i = 0; i < nCourse.getChildNodes().getLength(); i++) {
Node compoundMark = nCourse.getChildNodes().item(i);
if(compoundMark.getNodeName().equals("CompoundMark")) {
int compoundMarkID = getCompoundMarkID((Element) compoundMark);
compoundMarkMap.put(compoundMarkID, (Element)compoundMark);
compoundMarks.add(getCompoundMark(compoundMarkID));
}
}
}
/**
* Generates a CompoundMark from the CompoundMark element with given ID.
* @param compoundMarkID index of required CompoundMark element
* @return generated CompoundMark
* @throws StreamedCourseXMLException if CompoundMark element contains unhandled number of compoundMarks
* @see CompoundMark
*/
private CompoundMark getCompoundMark(int compoundMarkID) throws StreamedCourseXMLException {
Element compoundMark = compoundMarkMap.get(compoundMarkID);
NodeList nMarks = compoundMark.getElementsByTagName("Mark");
CompoundMark marker;
switch(nMarks.getLength()) {
case 1: marker = new CompoundMark(getMark((Element)nMarks.item(0)));
break;
case 2: marker = new CompoundMark(getMark((Element)nMarks.item(0)), getMark((Element)nMarks.item(1))); break;
default: throw new StreamedCourseXMLException();
}
return marker;
}
/**
* Gets a mark from an Element
* @param mark Element the mark is suppose to be part of
* @return a Mark that existed in the element
*/
private Mark getMark(Element mark) {
int sourceID = Integer.parseInt(mark.getAttribute("SourceID"));
return marks.get(sourceID);
}
/**
* Reads "compoundMarkID" attribute of CompoundMark or Corner element
* @param element with "compoundMarkID" attribute
* @return value of "compoundMarkID" attribute
*/
private int getCompoundMarkID(Element element) {
return Integer.parseInt(element.getAttribute("CompoundMarkID"));
}
/**
* Reads "name" attribute of CompoundMark element with corresponding CompoundMarkID
* @param compoundMarkID unique ID for CompoundMark element
* @return value of "name" attribute
*/
private String getCompoundMarkName(int compoundMarkID) {
return compoundMarkMap.get(compoundMarkID).getAttribute("Name");
}
/**
* Populates list of legs given CompoundMarkSequence element and referenced CompoundMark elements.
* @throws StreamedCourseXMLException if compoundMarks cannot be resolved from CompoundMark
*/
private void readCompoundMarkSequence() throws StreamedCourseXMLException {
Element nCompoundMarkSequence = (Element) doc.getElementsByTagName("CompoundMarkSequence").item(0);
NodeList nCorners = nCompoundMarkSequence.getElementsByTagName("Corner");
Element markXML = (Element)nCorners.item(0);
CompoundMark lastCompoundMark = getCompoundMark(getCompoundMarkID(markXML));
String legName = getCompoundMarkName(getCompoundMarkID(markXML));
for(int i = 1; i < nCorners.getLength(); i++) {
markXML = (Element)nCorners.item(i);
CompoundMark currentCompoundMark = getCompoundMark(getCompoundMarkID(markXML));
legs.add(new Leg(legName, lastCompoundMark, currentCompoundMark, i-1));
lastCompoundMark = currentCompoundMark;
legName = getCompoundMarkName(getCompoundMarkID(markXML));
}
}
/**
* Reads the boundary limits of the course
*/
private void readCourseLimit() {
Element nCourseLimit = (Element) doc.getElementsByTagName("CourseLimit").item(0);
for(int i = 0; i < nCourseLimit.getChildNodes().getLength(); i++) {
Node limit = nCourseLimit.getChildNodes().item(i);
if (limit.getNodeName().equals("Limit")) {
double lat = Double.parseDouble(limit.getAttributes().getNamedItem("Lat").getTextContent());
double lon = Double.parseDouble(limit.getAttributes().getNamedItem("Lon").getTextContent());
boundary.add(new GPSCoordinate(lat, lon));
}
}
double maxLatitude = boundary.stream().max(Comparator.comparingDouble(GPSCoordinate::getLatitude)).get().getLatitude() + COORDINATEPADDING;
double maxLongitude = boundary.stream().max(Comparator.comparingDouble(GPSCoordinate::getLongitude)).get().getLongitude() + COORDINATEPADDING;
double minLatitude = boundary.stream().min(Comparator.comparingDouble(GPSCoordinate::getLatitude)).get().getLatitude() + COORDINATEPADDING;
double minLongitude = boundary.stream().min(Comparator.comparingDouble(GPSCoordinate::getLongitude)).get().getLongitude() + COORDINATEPADDING;
mapTopLeft = new GPSCoordinate(minLatitude, minLongitude);
mapBottomRight = new GPSCoordinate(maxLatitude, maxLongitude);
}
public List<GPSCoordinate> getBoundary() {
return boundary;
}
public GPSCoordinate getMapTopLeft() {
return mapTopLeft;
}
public GPSCoordinate getMapBottomRight() {
return mapBottomRight;
}
public List<Leg> getLegs() {
return legs;
}
public List<CompoundMark> getCompoundMarks() { return compoundMarks; }
public Double getPadding() {
return COORDINATEPADDING;
}
public ZonedDateTime getCreationTimeDate() {
return creationTimeDate;
}
public ZonedDateTime getZonedDateTime() {
return raceStartTime;
}
public int getRaceId() {
return raceID;
}
public String getRaceType() {
return raceType;
}
public boolean isPostpone() {
return postpone;
}
public List<Boat> getBoats() {
return new ArrayList<>(participants.values());
}
}