mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 14:28:43 +00:00
Merge branch 'Story29' into merge_branch_front
# Conflicts: # src/main/java/seng302/App.java # src/main/java/seng302/controllers/CanvasController.java # src/main/java/seng302/controllers/Controller.java # src/main/java/seng302/controllers/RaceViewController.java # src/main/java/seng302/models/Boat.java # src/main/java/seng302/models/Colors.java # src/main/java/seng302/models/Event.java # src/main/java/seng302/models/Race.java # src/main/java/seng302/models/mark/GateMark.java # src/main/java/seng302/models/mark/Mark.java # src/main/java/seng302/models/mark/MarkType.java # src/main/java/seng302/models/mark/SingleMark.java # src/main/java/seng302/models/parsers/CourseParser.java # src/main/java/seng302/models/parsers/TeamsParser.java # src/main/resources/views/MainView.fxml # src/main/resources/views/RaceView.fxml # src/test/java/seng302/BoatTest.java # src/test/java/seng302/ColorsTest.java # src/test/java/seng302/EventTest.java # src/test/java/seng302/models/mark/MarkTest.java # src/test/java/seng302/models/parsers/CourseParserTest.java
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
package seng302.server.simulator;
|
||||
|
||||
import seng302.server.simulator.mark.Corner;
|
||||
import seng302.server.simulator.mark.Position;
|
||||
|
||||
public class Boat {
|
||||
|
||||
private int sourceID;
|
||||
private double lat;
|
||||
private double lng;
|
||||
private double speed; // in mm/sec
|
||||
private String boatName, shortName, shorterName;
|
||||
|
||||
private Corner lastPassedCorner, headingCorner;
|
||||
|
||||
public Boat(int sourceID, String boatName) {
|
||||
this.sourceID = sourceID;
|
||||
this.boatName = boatName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves boat to the heading direction for a given time duration
|
||||
* @param heading moving direction in degree.
|
||||
* @param duration moving duration in millisecond.
|
||||
*/
|
||||
public void move(double heading, double duration) {
|
||||
Double distance = speed * duration / 1000000; // convert mm to meter
|
||||
Position originPos = new Position(lat, lng);
|
||||
Position newPos = GeoUtility.getGeoCoordinate(originPos, heading, distance);
|
||||
this.lat = newPos.getLat();
|
||||
this.lng = newPos.getLng();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("Boat (%d): lat: %f, lng: %f", sourceID, lat, lng);
|
||||
}
|
||||
|
||||
public int getSourceID() {
|
||||
return sourceID;
|
||||
}
|
||||
|
||||
public void setSourceID(int sourceID) {
|
||||
this.sourceID = sourceID;
|
||||
}
|
||||
|
||||
public double getLat() {
|
||||
return lat;
|
||||
}
|
||||
|
||||
public void setLat(double lat) {
|
||||
this.lat = lat;
|
||||
}
|
||||
|
||||
public double getLng() {
|
||||
return lng;
|
||||
}
|
||||
|
||||
public void setLng(double lng) {
|
||||
this.lng = lng;
|
||||
}
|
||||
|
||||
public double getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
public void setSpeed(double speed) {
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
public String getBoatName() {
|
||||
return boatName;
|
||||
}
|
||||
|
||||
public void setBoatName(String boatName) {
|
||||
this.boatName = boatName;
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
public void setShortName(String shortName) {
|
||||
this.shortName = shortName;
|
||||
}
|
||||
|
||||
public String getShorterName() {
|
||||
return shorterName;
|
||||
}
|
||||
|
||||
public void setShorterName(String shorterName) {
|
||||
this.shorterName = shorterName;
|
||||
}
|
||||
|
||||
public Corner getLastPassedCorner() {
|
||||
return lastPassedCorner;
|
||||
}
|
||||
|
||||
public void setLastPassedCorner(Corner lastPassedCorner) {
|
||||
this.lastPassedCorner = lastPassedCorner;
|
||||
}
|
||||
|
||||
public Corner getHeadingCorner() {
|
||||
return headingCorner;
|
||||
}
|
||||
|
||||
public void setHeadingCorner(Corner headingCorner) {
|
||||
this.headingCorner = headingCorner;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package seng302.server.simulator;
|
||||
|
||||
import seng302.server.simulator.mark.Position;
|
||||
|
||||
public class GeoUtility {
|
||||
|
||||
private static double EARTH_RADIUS = 6378.137;
|
||||
|
||||
/**
|
||||
* Calculates the euclidean distance between two markers on the canvas using xy coordinates
|
||||
*
|
||||
* @param p1 first geographical position
|
||||
* @param p2 second geographical position
|
||||
* @return the distance in meter between two points in meters
|
||||
*/
|
||||
public static Double getDistance(Position p1, Position p2) {
|
||||
|
||||
double dLat = Math.toRadians(p2.getLat() - p1.getLat());
|
||||
double dLon = Math.toRadians(p2.getLng() - p1.getLng());
|
||||
|
||||
double a = Math.pow(Math.sin(dLat / 2), 2.0)
|
||||
+ Math.cos(Math.toRadians(p1.getLat())) * Math.cos(Math.toRadians(p2.getLat()))
|
||||
* Math.pow(Math.sin(dLon / 2), 2.0);
|
||||
|
||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
double d = EARTH_RADIUS * c;
|
||||
|
||||
return d * 1000; // distance from km to meter
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the angle between to angular co-ordinates on a sphere.
|
||||
*
|
||||
* @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 getBearing(Position p1, Position 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);
|
||||
|
||||
double bearing = Math.toDegrees(Math.atan2(y, x));
|
||||
|
||||
return (bearing + 360.0) % 360.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an existing point in lat/lng, distance in (in meter) and bearing
|
||||
* (in degrees), calculates the new lat/lng.
|
||||
*
|
||||
* @param origin the original position within lat / lng
|
||||
* @param bearing the bearing in degree, from original position to the new position
|
||||
* @param distance the distance in meter, from original position to the new position
|
||||
* @return the new position
|
||||
*/
|
||||
public static Position getGeoCoordinate(Position origin, Double bearing, Double distance) {
|
||||
double b = Math.toRadians(bearing); // bearing to radians
|
||||
double d = distance / 1000.0; // distance to km
|
||||
|
||||
double originLat = Math.toRadians(origin.getLat());
|
||||
double originLng = Math.toRadians(origin.getLng());
|
||||
|
||||
double endLat = Math.asin(Math.sin(originLat) * Math.cos(d / EARTH_RADIUS)
|
||||
+ Math.cos(originLat) * Math.sin(d / EARTH_RADIUS) * Math.cos(b));
|
||||
double endLng = originLng
|
||||
+ Math.atan2(Math.sin(b) * Math.sin(d / EARTH_RADIUS) * Math.cos(originLat),
|
||||
Math.cos(d / EARTH_RADIUS) - Math.sin(originLat) * Math.sin(endLat));
|
||||
|
||||
return new Position(Math.toDegrees(endLat), Math.toDegrees(endLng));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package seng302.server.simulator;
|
||||
|
||||
import seng302.server.simulator.mark.Corner;
|
||||
import seng302.server.simulator.mark.Mark;
|
||||
import seng302.server.simulator.mark.Position;
|
||||
import seng302.server.simulator.parsers.RaceParser;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Observable;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class Simulator extends Observable implements Runnable {
|
||||
|
||||
private List<Corner> course;
|
||||
private List<Boat> boats;
|
||||
private long lapse;
|
||||
|
||||
/**
|
||||
* Creates a simulator instance with given time lapse.
|
||||
* @param lapse time duration in millisecond.
|
||||
*/
|
||||
public Simulator(long lapse) {
|
||||
RaceParser rp = new RaceParser("/server_config/race.xml");
|
||||
course = rp.getCourse();
|
||||
boats = rp.getBoats();
|
||||
this.lapse = lapse;
|
||||
|
||||
setLegs();
|
||||
|
||||
// set start line's coordinate to boats
|
||||
Double startLat = course.get(0).getCompoundMark().getMark1().getLat();
|
||||
Double startLng = course.get(0).getCompoundMark().getMark1().getLng();
|
||||
for (Boat boat : boats) {
|
||||
boat.setLat(startLat);
|
||||
boat.setLng(startLng);
|
||||
boat.setLastPassedCorner(course.get(0));
|
||||
boat.setHeadingCorner(course.get(1));
|
||||
boat.setSpeed(ThreadLocalRandom.current().nextInt(40000, 60000 + 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
int numOfFinishedBoats = 0;
|
||||
|
||||
while (numOfFinishedBoats < boats.size()) {
|
||||
for (Boat boat : boats) {
|
||||
numOfFinishedBoats += moveBoat(boat, lapse);
|
||||
}
|
||||
//System.out.println(boats.get(0));
|
||||
|
||||
setChanged();
|
||||
notifyObservers(boats);
|
||||
|
||||
// sleep for 1 second.
|
||||
try {
|
||||
Thread.sleep(lapse);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a boat with given time duration.
|
||||
*
|
||||
* @param boat the boat to be moved
|
||||
* @param duration the moving duration in milliseconds
|
||||
* @return 1 if the boat has reached the final line, otherwise return 0
|
||||
*/
|
||||
private int moveBoat(Boat boat, double duration) {
|
||||
if (boat.getHeadingCorner() != null) {
|
||||
|
||||
boat.move(boat.getLastPassedCorner().getBearingToNextCorner(), duration);
|
||||
|
||||
Position boatPos = new Position(boat.getLat(), boat.getLng());
|
||||
Position lastMarkPos = boat.getLastPassedCorner().getCompoundMark().getMark1();
|
||||
|
||||
double distanceFromLastMark = GeoUtility.getDistance(boatPos, lastMarkPos);
|
||||
// if a boat passes its heading mark
|
||||
while (distanceFromLastMark >= boat.getLastPassedCorner().getDistanceToNextCorner()) {
|
||||
double compensateDistance = distanceFromLastMark - boat.getLastPassedCorner().getDistanceToNextCorner();
|
||||
boat.setLastPassedCorner(boat.getHeadingCorner());
|
||||
boat.setHeadingCorner(boat.getLastPassedCorner().getNextCorner());
|
||||
|
||||
// heading corner == null means boat has reached the final mark
|
||||
if (boat.getHeadingCorner() == null) return 1;
|
||||
|
||||
// move compensate distance for the mark just passed
|
||||
Position pos = GeoUtility.getGeoCoordinate(
|
||||
boat.getLastPassedCorner().getCompoundMark().getMark1(),
|
||||
boat.getLastPassedCorner().getBearingToNextCorner(),
|
||||
compensateDistance);
|
||||
boat.setLat(pos.getLat());
|
||||
boat.setLng(pos.getLng());
|
||||
distanceFromLastMark = GeoUtility.getDistance(new Position(boat.getLat(), boat.getLng()),
|
||||
boat.getLastPassedCorner().getCompoundMark().getMark1());
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link all the corners in the course list so that every corner knows its next
|
||||
* corner, as well as the distance and bearing to its next corner. However,
|
||||
* the last corner's heading is null, which means it is the final line.
|
||||
*/
|
||||
private void setLegs() {
|
||||
// get the bearing from one mark to the next heading mark
|
||||
for (int i = 0; i < course.size() - 1; i++) {
|
||||
|
||||
Mark mark1 = course.get(i).getCompoundMark().getMark1();
|
||||
Mark mark2 = course.get(i + 1).getCompoundMark().getMark1();
|
||||
course.get(i).setDistanceToNextCorner(GeoUtility.getDistance(mark1, mark2));
|
||||
|
||||
course.get(i).setNextCorner(course.get(i + 1));
|
||||
|
||||
course.get(i).setBearingToNextCorner(
|
||||
GeoUtility.getBearing(course.get(i).getCompoundMark().getMark1(),
|
||||
course.get(i + 1).getCompoundMark().getMark1()));
|
||||
}
|
||||
}
|
||||
|
||||
public List<Boat> getBoats(){
|
||||
return boats;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package seng302.server.simulator.mark;
|
||||
|
||||
public class Corner {
|
||||
|
||||
private int seqID;
|
||||
private CompoundMark compoundMark;
|
||||
//private int CompoundMarkID;
|
||||
private RoundingType roundingType;
|
||||
private int zoneSize; // size of the zone around a mark in boat-lengths.
|
||||
|
||||
// TODO: this shouldn't be used in the future!!!!
|
||||
private double bearingToNextCorner, distanceToNextCorner;
|
||||
private Corner nextCorner;
|
||||
|
||||
public Corner(int seqID, CompoundMark compoundMark, RoundingType roundingType, int zoneSize) {
|
||||
this.seqID = seqID;
|
||||
this.compoundMark = compoundMark;
|
||||
this.roundingType = roundingType;
|
||||
this.zoneSize = zoneSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints out corner's info and its compound mark, good for testing
|
||||
* @return a string showing its details
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Corner: %d - %s - %d, %s\n",
|
||||
seqID, roundingType.getType(), zoneSize, compoundMark.toString());
|
||||
}
|
||||
|
||||
public int getSeqID() {
|
||||
return seqID;
|
||||
}
|
||||
|
||||
public void setSeqID(int seqID) {
|
||||
this.seqID = seqID;
|
||||
}
|
||||
|
||||
public CompoundMark getCompoundMark() {
|
||||
return compoundMark;
|
||||
}
|
||||
|
||||
public void setCompoundMark(CompoundMark compoundMark) {
|
||||
this.compoundMark = compoundMark;
|
||||
}
|
||||
|
||||
public RoundingType getRoundingType() {
|
||||
return roundingType;
|
||||
}
|
||||
|
||||
public void setRoundingType(RoundingType roundingType) {
|
||||
this.roundingType = roundingType;
|
||||
}
|
||||
|
||||
public int getZoneSize() {
|
||||
return zoneSize;
|
||||
}
|
||||
|
||||
public void setZoneSize(int zoneSize) {
|
||||
this.zoneSize = zoneSize;
|
||||
}
|
||||
|
||||
|
||||
// TODO: next six setters & getters shouldn't be used in the future.
|
||||
public double getBearingToNextCorner() {
|
||||
return bearingToNextCorner;
|
||||
}
|
||||
|
||||
public void setBearingToNextCorner(double bearingToNextCorner) {
|
||||
this.bearingToNextCorner = bearingToNextCorner;
|
||||
}
|
||||
|
||||
public double getDistanceToNextCorner() {
|
||||
return distanceToNextCorner;
|
||||
}
|
||||
|
||||
public void setDistanceToNextCorner(double distanceToNextCorner) {
|
||||
this.distanceToNextCorner = distanceToNextCorner;
|
||||
}
|
||||
|
||||
public Corner getNextCorner() {
|
||||
return nextCorner;
|
||||
}
|
||||
|
||||
public void setNextCorner(Corner nextCorner) {
|
||||
this.nextCorner = nextCorner;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package seng302.server.simulator.mark;
|
||||
|
||||
/**
|
||||
* An abstract class to represent general marks
|
||||
* Created by Haoming Yin (hyi25) on 17/3/17.
|
||||
*/
|
||||
public class Mark extends Position {
|
||||
|
||||
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, lat, lng);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package seng302.server.simulator.mark;
|
||||
|
||||
public class Position {
|
||||
|
||||
double lat, lng;
|
||||
|
||||
public Position(double lat, double lng) {
|
||||
this.lat = lat;
|
||||
this.lng = lng;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("Position at lat:%f lng:%f.", lat, lng);
|
||||
}
|
||||
|
||||
public double getLat() {
|
||||
return lat;
|
||||
}
|
||||
|
||||
public void setLat(double lat) {
|
||||
this.lat = lat;
|
||||
}
|
||||
|
||||
public double getLng() {
|
||||
return lng;
|
||||
}
|
||||
|
||||
public void setLng(double lng) {
|
||||
this.lng = lng;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package seng302.server.simulator.mark;
|
||||
|
||||
public enum RoundingType {
|
||||
|
||||
// the mark should be rounded to port (boat's left)
|
||||
PORT("Port"),
|
||||
|
||||
// the mark should be rounded to starboard (boat's right)
|
||||
STARBOARD("Stbd"),
|
||||
|
||||
// the boat within the compound mark with the SeqID of 1 should be rounded
|
||||
// to starboard and the boat within the compound mark with the SeqID of 2
|
||||
// should be rounded to port.
|
||||
SP("SP"),
|
||||
|
||||
// the opposite of SP
|
||||
PS("PS");
|
||||
|
||||
private String type;
|
||||
|
||||
RoundingType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public static RoundingType typeOf(String type) {
|
||||
switch (type) {
|
||||
case "Port":
|
||||
return PORT;
|
||||
case "Stbd":
|
||||
return STARBOARD;
|
||||
case "SP":
|
||||
return SP;
|
||||
case "PS":
|
||||
return PS;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package seng302.server.simulator.parsers;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
|
||||
/**
|
||||
* Parses the race xml file to get course details
|
||||
* Created by Haoming Yin (hyi25) on 16/3/2017
|
||||
*/
|
||||
public class BoatsParser extends FileParser {
|
||||
|
||||
private Document doc;
|
||||
|
||||
public BoatsParser(String path) {
|
||||
super(path);
|
||||
this.doc = this.parseFile();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package seng302.server.simulator.parsers;
|
||||
|
||||
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 java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Parses the race xml file to get course details
|
||||
* Created by Haoming Yin (hyi25) on 16/3/2017
|
||||
*/
|
||||
public class CourseParser extends FileParser {
|
||||
|
||||
private Document doc;
|
||||
private Map<Integer, CompoundMark> compoundMarksMap;
|
||||
|
||||
public CourseParser(String path) {
|
||||
super(path);
|
||||
this.doc = this.parseFile();
|
||||
}
|
||||
|
||||
// TODO: should handle error / invalid file gracefully
|
||||
protected List<Corner> getCourse() {
|
||||
compoundMarksMap = getCompoundMarks(doc.getDocumentElement());
|
||||
List<Corner> corners = new ArrayList<>();
|
||||
NodeList cMarksSequence = doc.getElementsByTagName("Corner");
|
||||
|
||||
for (int i = 0; i < cMarksSequence.getLength(); i++) {
|
||||
corners.add(getCorner(cMarksSequence.item(i)));
|
||||
}
|
||||
return corners;
|
||||
}
|
||||
|
||||
|
||||
private Corner getCorner(Node node) {
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element e = (Element) node;
|
||||
|
||||
Integer seqId = Integer.valueOf(e.getAttribute("SeqID"));
|
||||
Integer cMarkId = Integer.valueOf(e.getAttribute("CompoundMarkID"));
|
||||
CompoundMark cMark = compoundMarksMap.get(cMarkId);
|
||||
RoundingType roundingType = RoundingType.typeOf(e.getAttribute("Rounding"));
|
||||
Integer zoneSize = Integer.valueOf(e.getAttribute("ZoneSize"));
|
||||
|
||||
return new Corner(seqId, cMark, roundingType, zoneSize);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<Integer, CompoundMark> getCompoundMarks(Node node) {
|
||||
Map<Integer, CompoundMark> compoundMarksMap = new HashMap<>();
|
||||
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element element = (Element) node;
|
||||
NodeList cMarks = element.getElementsByTagName("CompoundMark");
|
||||
|
||||
// loop through all compound marks who are the children of course node
|
||||
for (int i = 0; i < cMarks.getLength(); i++) {
|
||||
CompoundMark cMark = getCompoundMark(cMarks.item(i));
|
||||
if (cMark != null)
|
||||
compoundMarksMap.put(cMark.getMarkID(), cMark);
|
||||
}
|
||||
|
||||
return compoundMarksMap;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private CompoundMark getCompoundMark(Node node) {
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element e = (Element) node;
|
||||
Integer markID = Integer.valueOf(e.getAttribute("CompoundMarkID"));
|
||||
|
||||
String name = e.getAttribute("Name");
|
||||
CompoundMark cMark = new CompoundMark(markID, name);
|
||||
|
||||
NodeList marks = e.getElementsByTagName("Mark");
|
||||
for (int i = 0; i < marks.getLength(); i++) {
|
||||
Mark mark = getMark(marks.item(i));
|
||||
if (mark != null)
|
||||
cMark.addMark(mark.getSeqID(), mark);
|
||||
}
|
||||
return cMark;
|
||||
}
|
||||
System.out.println("Failed to create compound mark.");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private Mark getMark(Node node) {
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element e = (Element) node;
|
||||
Integer seqId = Integer.valueOf(e.getAttribute("SeqID"));
|
||||
String name = e.getAttribute("Name");
|
||||
Double lat = Double.valueOf(e.getAttribute("TargetLat"));
|
||||
Double lng = Double.valueOf(e.getAttribute("TargetLng"));
|
||||
Integer sourceId = Integer.valueOf(e.getAttribute("SourceID"));
|
||||
|
||||
Mark mark = new Mark(name, lat, lng, sourceId);
|
||||
mark.setSeqID(seqId);
|
||||
|
||||
return mark;
|
||||
}
|
||||
System.out.println("Failed to create mark.");
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package seng302.server.simulator.parsers;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* Created by Haoming Yin (hyi25) on 16/3/2017
|
||||
*/
|
||||
public abstract class FileParser {
|
||||
|
||||
private String filePath;
|
||||
|
||||
public FileParser() {}
|
||||
|
||||
public FileParser(String path) {
|
||||
this.filePath = path;
|
||||
}
|
||||
|
||||
protected Document parseFile() {
|
||||
try {
|
||||
InputStream is = getClass().getResourceAsStream(this.filePath);
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.parse(is);
|
||||
// optional, in order to recover info from broken line.
|
||||
doc.getDocumentElement().normalize();
|
||||
return doc;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected Document parseFile(String xmlString) {
|
||||
try {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.parse(new InputSource(new StringReader(xmlString)));
|
||||
// optional, in order to recover info from broken line.
|
||||
doc.getDocumentElement().normalize();
|
||||
return doc;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package seng302.server.simulator.parsers;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Parses the race xml file to get course details
|
||||
* Created by Haoming Yin (hyi25) on 16/3/2017
|
||||
*/
|
||||
public class RaceParser extends FileParser {
|
||||
|
||||
private Document doc;
|
||||
private String path;
|
||||
|
||||
public RaceParser(String path) {
|
||||
super(path);
|
||||
this.path = path;
|
||||
this.doc = this.parseFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses race.xml file and returns a list of corner which is the race course.
|
||||
* @return a list of ordered corner to represent the course.
|
||||
*/
|
||||
public List<Corner> getCourse() {
|
||||
CourseParser cp = new CourseParser(path);
|
||||
return cp.getCourse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses race.xml file and return a list of boats which will compete in the
|
||||
* race.
|
||||
* @return a list of boats that are going to compete in the race.
|
||||
*/
|
||||
public List<Boat> getBoats() {
|
||||
NodeList yachts = doc.getDocumentElement().getElementsByTagName("Yacht");
|
||||
List<Boat> boats = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < yachts.getLength(); i++) {
|
||||
boats.add(getBoat(yachts.item(i)));
|
||||
}
|
||||
return boats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a single boat from the given node
|
||||
* @param node a node within a boat tag
|
||||
* @return a boat instance parsed from the given node
|
||||
*/
|
||||
private Boat getBoat(Node node) {
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element e = (Element) node;
|
||||
|
||||
Integer sourceId = Integer.valueOf(e.getAttribute("SourceID"));
|
||||
return new Boat(sourceId, "Test Boat");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user