@ -1,17 +1,16 @@
package mock.xml ;
package mock.xml ;
import mock.model.NewPolars ;
import org.xml.sax.SAXException ;
import org.xml.sax.SAXException ;
import shared.dataInput.RaceXMLReader ;
import shared.dataInput.RaceXMLReader ;
import shared.enums.XMLFileType ;
import shared.enums.XMLFileType ;
import shared.exceptions.InvalidRaceDataException ;
import shared.exceptions.InvalidRaceDataException ;
import shared.exceptions.XMLReaderException ;
import shared.exceptions.XMLReaderException ;
import shared.model.Bearing ;
import shared.model.CompoundMark ;
import shared.model.CompoundMark ;
import shared.model.Constants ;
import shared.model.Constants ;
import shared.model.GPSCoordinate ;
import shared.model.GPSCoordinate ;
import shared.xml.Race.XMLCompoundMark ;
import shared.xml.Race.* ;
import shared.xml.Race.XMLLimit ;
import shared.xml.Race.XMLMark ;
import shared.xml.Race.XMLRace ;
import shared.xml.XMLUtilities ;
import shared.xml.XMLUtilities ;
import javax.xml.bind.JAXBException ;
import javax.xml.bind.JAXBException ;
@ -20,6 +19,8 @@ import java.io.IOException;
import java.io.InputStream ;
import java.io.InputStream ;
import java.time.ZonedDateTime ;
import java.time.ZonedDateTime ;
import java.time.format.DateTimeFormatter ;
import java.time.format.DateTimeFormatter ;
import java.util.Collections ;
import java.util.Comparator ;
/ * *
/ * *
* Helper Class for creating a Race XML
* Helper Class for creating a Race XML
@ -125,6 +126,140 @@ public class RaceXMLCreator {
}
}
}
}
/ * *
* Rotates the race in a specified direction .
* @param s xml file name or contents .
* @param windSpeed speed that the wind is at .
* @param milliseconds time the race should take at fastest
* @return the new xml file as a string
* @throws XMLReaderException if the xml is not readable
* @throws InvalidRaceDataException if the race is invalid
* /
public static String scaleRaceSize ( String s , double windSpeed , double milliseconds ) throws XMLReaderException , InvalidRaceDataException {
try {
XMLRace race = XMLUtilities . xmlToClass (
s ,
RaceXMLCreator . class . getClassLoader ( ) . getResource ( "mock/mockXML/schema/raceSchema.xsd" ) ,
XMLRace . class ) ;
scaleRace ( race , windSpeed , milliseconds ) ;
return XMLUtilities . classToXML ( race ) ;
} catch ( ParserConfigurationException | IOException | SAXException | JAXBException e ) {
throw new InvalidRaceDataException ( "Could not parse or marshall race data file." , e ) ;
}
}
/ * *
* Gets an estimate of how long a race will take using an average Speed
* @param race race to estimate
* @param averageSpeed average speed that the boats move at
* @return the estimated amount of time it will take a boat to finish the race ( skewed to minimum ) .
* /
public static double getRaceLength ( XMLRace race , double averageSpeed ) {
double raceRoundingTime = 5000 ; //5 seconds to round a mark
double totalDistance = 0 ; //in nautical miles
XMLMark prevMark = null ;
double avgSpeed = averageSpeed / 60 / 60 ; //knots is /hour
for ( XMLCorner corner : race . getCompoundMarkSequence ( ) . getCorner ( ) ) {
int index = corner . getCompoundMarkID ( ) - 1 ;
XMLCompoundMark cm = race . getCourse ( ) . getCompoundMark ( ) . get ( index ) ;
XMLMark mark = cm . getMark ( ) . get ( 0 ) ;
if ( prevMark ! = null ) {
totalDistance + = getDistance ( mark , prevMark ) ;
}
prevMark = mark ;
}
//total time = total dist / average speed + race extra rounding time * number of marks
double totalTime = totalDistance / avgSpeed * 1000 +
raceRoundingTime * race . getCompoundMarkSequence ( ) . getCorner ( ) . size ( ) ;
return totalTime ;
}
/ * *
* gets the destance between two marks
* @param a mark 1
* @param b mark 2
* @return
* /
private static double getDistance ( XMLMark a , XMLMark b ) {
GPSCoordinate coorda = new GPSCoordinate ( a . getTargetLat ( ) , a . getTargetLng ( ) ) ;
GPSCoordinate coordb = new GPSCoordinate ( b . getTargetLat ( ) , b . getTargetLng ( ) ) ;
return GPSCoordinate . calculateDistanceNauticalMiles ( coorda , coordb ) ;
}
/ * *
* Scales the race based on the windspeed the race is running at and the amount of time it should be completed in .
* @param race Race to scale
* @param windSpeed windspeed of the race , this is used with the polars
* @param milliseconds milliseconds the race should take .
* /
private static void scaleRace ( XMLRace race , double windSpeed , double milliseconds ) {
GPSCoordinate center = getCenter ( race ) ;
//sort the compound marks
Collections . sort ( race . getCompoundMarkSequence ( ) . getCorner ( ) , ( c1 , c2 ) - > {
if ( c1 . getSeqID ( ) < c2 . getSeqID ( ) ) return - 1 ;
if ( c1 . getSeqID ( ) > c2 . getSeqID ( ) ) return 1 ;
return 0 ;
} ) ;
//sort compound mark id
Collections . sort ( race . getCourse ( ) . getCompoundMark ( ) , ( c1 , c2 ) - > {
if ( c1 . getCompoundMarkID ( ) < c2 . getCompoundMarkID ( ) ) return - 1 ;
if ( c1 . getCompoundMarkID ( ) > c2 . getCompoundMarkID ( ) ) return 1 ;
return 0 ;
} ) ;
//get the fastest time it would take.
double bestUpWindSpeed = NewPolars . setBestVMG ( Bearing . fromDegrees ( 0 ) , windSpeed , Bearing . fromDegrees ( 45 ) ) . getSpeed ( ) ;
double bestDownWindSpeed = NewPolars . setBestVMG ( Bearing . fromDegrees ( 0 ) , windSpeed , Bearing . fromDegrees ( 45 ) ) . getSpeed ( ) ;
double averageSpeed = ( bestDownWindSpeed + bestUpWindSpeed ) / 2 ;
double raceApproximateTime = getRaceLength ( race , averageSpeed ) ;
double scale = milliseconds / raceApproximateTime ;
for ( XMLCorner cm : race . getCompoundMarkSequence ( ) . getCorner ( ) ) {
int index = cm . getCompoundMarkID ( ) - 1 ;
XMLCompoundMark mark = race . getCourse ( ) . getCompoundMark ( ) . get ( index ) ;
for ( XMLMark m : mark . getMark ( ) ) {
scalePoint ( m , center , scale ) ;
}
}
for ( XMLLimit limit : race . getCourseLimit ( ) . getLimit ( ) ) {
scalePoint ( limit , center , scale ) ;
}
}
/ * *
* Scales a point from the the center ( pivot )
* @param mark mark the scale
* @param center center as pivot
* @param scale scale to scale at .
* /
private static void scalePoint ( XMLMark mark , GPSCoordinate center , double scale ) {
double latDiff = mark . getTargetLat ( ) - center . getLatitude ( ) ;
double longDiff = mark . getTargetLng ( ) - center . getLongitude ( ) ;
double latScaled = latDiff * scale + center . getLatitude ( ) ;
double longScaled = longDiff * scale + center . getLongitude ( ) ;
mark . setTargetLat ( latScaled ) ;
mark . setTargetLng ( longScaled ) ;
}
/ * *
* Scales a boundary from the center ( pivot )
* @param limit boundary point
* @param center pivot
* @param scale scale
* /
private static void scalePoint ( XMLLimit limit , GPSCoordinate center , double scale ) {
double latDiff = limit . getLat ( ) - center . getLatitude ( ) ;
double longDiff = limit . getLon ( ) - center . getLongitude ( ) ;
double latScaled = latDiff * scale + center . getLatitude ( ) ;
double longScaled = longDiff * scale + center . getLongitude ( ) ;
limit . setLat ( latScaled ) ;
limit . setLon ( longScaled ) ;
}
/ * *
/ * *
* Converts a Race . CourseLimit . Limit to a GPS coordinate
* Converts a Race . CourseLimit . Limit to a GPS coordinate
* @param limit limit to convert
* @param limit limit to convert