Merge branch 'racecommittee_app' into racecommittee_regattaoverview

This commit is contained in:
Armin Zamani committed 2013-06-16 18:40:10 +02:00
commit 7f148ed5a0
21 files changed
+470 -195

No files matched your search

@@ -0,0 +1,78 @@
package com.sap.sailing.domain.coursedesign;
import java.util.HashSet;
import java.util.Set;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
public abstract class AbstractCourseDesign implements CourseDesign{
Position startBoatPosition;
Double windSpeed;
Bearing windDirection;
PositionedMark pinEnd;
Position referencePoint;
Set<PositionedMark> courseDesignSpecificMarks = new HashSet<PositionedMark>();
@Override
public Set<PositionedMark> getCourseDesignSpecificMarks() {
return courseDesignSpecificMarks;
}
@Override
public void setCourseDesignSpecificMarks(Set<PositionedMark> courseDesignSpecificMarks) {
this.courseDesignSpecificMarks = courseDesignSpecificMarks;
}
@Override
public Position getReferencePoint() {
return referencePoint;
}
@Override
public void setReferencePoint(Position referencePoint) {
this.referencePoint = referencePoint;
}
@Override
public PositionedMark getPinEnd() {
return pinEnd;
}
@Override
public void setPinEnd(PositionedMark pinEnd) {
this.pinEnd = pinEnd;
}
@Override
public Position getStartBoatPosition() {
return this.startBoatPosition;
}
@Override
public void setStartBoatPosition(Position startBoatPosition) {
this.startBoatPosition = startBoatPosition;
}
@Override
public Double getWindSpeed() {
return this.windSpeed;
}
@Override
public void setWindSpeed(Double windSpeed) {
this.windSpeed = windSpeed;
}
@Override
public Bearing getWindDirection() {
return this.windDirection;
}
@Override
public void setWindDirection(Bearing windDirection) {
this.windDirection = windDirection;
}
}
@@ -1,27 +1,90 @@
package com.sap.sailing.domain.coursedesign;
import java.util.Set;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
public abstract class AbstractCourseDesignFactory implements CourseDesignFactory{
public abstract class AbstractCourseDesignFactory implements CourseDesignFactory {
protected CourseDesign product;
private final int ANGLE_OF_START_LINE_TO_WIND = 270;
private final Distance REFERENCE_POINT_DISTANCE_FROM_START_LINE = new NauticalMileDistance(0.05);
@Override
public abstract CourseDesign createCourseDesign(Position startBoatPosition, Double windSpeed, Integer windDirection,
abstract protected Set<PositionedMark> computeDesignSpecificMarks(Position startBoatPosition, Double windSpeed, Bearing windDirection,
BoatClassType boatClass, CourseLayouts courseLayout, NumberOfRounds numberOfRounds, TargetTime targetTime);
@Override
public abstract CourseDesign createCourseDesign(Position startBoatPosition, Double windSpeed,
Bearing windDirection, BoatClassType boatClass, CourseLayouts courseLayout, NumberOfRounds numberOfRounds,
TargetTime targetTime);
protected Position getPositionForGivenPointDistanceAndBearing(Position givenPoint, int ditanceInMeters,
float bearingInDegree) {
double bearingInRad = Math.toRadians(bearingInDegree);
final int earthRadiusInMeters = 6371000;
double lat = Math.asin(Math.sin(givenPoint.getLatDeg()) * Math.cos(ditanceInMeters / earthRadiusInMeters)
+ Math.cos(givenPoint.getLatDeg()) * Math.sin(ditanceInMeters / earthRadiusInMeters)
* Math.cos(bearingInRad));
double lon = givenPoint.getLngDeg()
+ Math.atan2(
Math.sin(bearingInRad) * Math.sin(ditanceInMeters / earthRadiusInMeters)
* Math.cos(givenPoint.getLatDeg()),
Math.cos(ditanceInMeters / earthRadiusInMeters) - Math.sin(givenPoint.getLatDeg()) * Math.sin(lat));
return new DegreePosition(lat, lon);
protected void initializeCourseDesign(Position startBoatPosition, Double windSpeed,
Bearing windDirection, BoatClassType boatClass, CourseLayouts courseLayout, NumberOfRounds numberOfRounds,
TargetTime targetTime){
this.product.setStartBoatPosition(startBoatPosition);
this.product.setWindSpeed(windSpeed);
this.product.setWindDirection(windDirection);
setPinEnd(boatClass, startBoatPosition, windDirection);
setReferencePoint(boatClass, startBoatPosition, windDirection);
}
protected void finalizeCourseDesign(Position startBoatPosition, Double windSpeed,
Bearing windDirection, BoatClassType boatClass, CourseLayouts courseLayout, NumberOfRounds numberOfRounds,
TargetTime targetTime){
this.product.getCourseDesignSpecificMarks().addAll(this.computeDesignSpecificMarks(startBoatPosition, windSpeed, windDirection, boatClass, courseLayout, numberOfRounds, targetTime));
}
protected void setPinEnd(BoatClassType boatClass, Position startBoatPosition, Bearing windDirection) {
PositionedMark pinEnd = new PositionedMarkImpl("pinEnd", getPositionForGivenPointDistanceAndBearing(
startBoatPosition, new MeterDistance(boatClass.getStartLineLengthInMeters()),
windDirection.add(new DegreeBearingImpl(ANGLE_OF_START_LINE_TO_WIND))));
product.setPinEnd(pinEnd);
}
protected void setReferencePoint(BoatClassType boatClass, Position startBoatPosition, Bearing windDirection) {
double halfOfStatLineLength = boatClass.getStartLineLengthInMeters()/2;
Position startLineMid = getPositionForGivenPointDistanceAndBearing(
startBoatPosition, new MeterDistance(halfOfStatLineLength),
windDirection.add(new DegreeBearingImpl(ANGLE_OF_START_LINE_TO_WIND)));
Position referencePoint = getPositionForGivenPointDistanceAndBearing(
startLineMid, REFERENCE_POINT_DISTANCE_FROM_START_LINE,
windDirection);
product.setReferencePoint(referencePoint);
}
protected Position getPositionForGivenPointDistanceAndBearing(Position givenPoint, Distance distance,
Bearing windDirection) {
final double earthRadiusInMeters = 6371000.0;
double brng = windDirection.getRadians();
double lat1 = givenPoint.getLatRad();
double lon1 = givenPoint.getLngRad();
double dist = distance.getMeters() / earthRadiusInMeters;
double lat2 = Math.asin(Math.sin(lat1) * Math.cos(dist) + Math.cos(lat1) * Math.sin(dist) * Math.cos(brng));
double lon2 = lon1
+ Math.atan2(Math.sin(brng) * Math.sin(dist) * Math.cos(lat1),
Math.cos(dist) - Math.sin(lat1) * Math.sin(lat2));
lon2 = (lon2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI;
return new DegreePosition(Math.toDegrees(lat2), Math.toDegrees(lon2));
/*
* LatLon.prototype.destinationPoint = function(brng, dist) { dist = typeof(dist)=='number' ? dist :
* typeof(dist)=='string' && dist.trim()!='' ? +dist : NaN; dist = dist/this._radius; // convert dist to angular
* distance in radians brng = brng.toRad(); // var lat1 = this._lat.toRad(), lon1 = this._lon.toRad();
*
* var lat2 = Math.asin( Math.sin(lat1)*Math.cos(dist) + Math.cos(lat1)*Math.sin(dist)*Math.cos(brng) ); var
* lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(dist)*Math.cos(lat1),
* Math.cos(dist)-Math.sin(lat1)*Math.sin(lat2)); lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI; // normalise
* to -180..+180º
*
* return new LatLon(lat2.toDeg(), lon2.toDeg()); }
*/
}
}
@@ -10,8 +10,8 @@ public enum BoatClassType {
{
put(TrapezoidCourseLayouts.innerLoopTrapezoid60, 60);
put(TrapezoidCourseLayouts.outerLoopTrapezoid60, 60);
put(LeewardWindWardCourseLayouts.windWardLeewardLeeward, 30);
put(LeewardWindWardCourseLayouts.windWardLeewardWindward, 30);
put(WindWardLeeWardCourseLayouts.windWardLeewardLeeward, 30);
put(WindWardLeeWardCourseLayouts.windWardLeewardWindward, 30);
}
}, new TreeMap<WindRange, Map<PointOfSail, Float>>() {
{
@@ -49,8 +49,8 @@ public enum BoatClassType {
{
put(TrapezoidCourseLayouts.innerLoopTrapezoid60, 60);
put(TrapezoidCourseLayouts.outerLoopTrapezoid60, 60);
put(LeewardWindWardCourseLayouts.windWardLeewardLeeward, 30);
put(LeewardWindWardCourseLayouts.windWardLeewardWindward, 30);
put(WindWardLeeWardCourseLayouts.windWardLeewardLeeward, 30);
put(WindWardLeeWardCourseLayouts.windWardLeewardWindward, 30);
}
}, new TreeMap<WindRange, Map<PointOfSail, Float>>() {
{
@@ -86,8 +86,8 @@ public enum BoatClassType {
}, 110),
boatClass49er("49er", new HashMap<CourseLayouts, Integer>() {
{
put(LeewardWindWardCourseLayouts.windWardLeewardLeeward, 30);
put(LeewardWindWardCourseLayouts.windWardLeewardWindward, 30);
put(WindWardLeeWardCourseLayouts.windWardLeewardLeeward, 30);
put(WindWardLeeWardCourseLayouts.windWardLeewardWindward, 30);
}
}, new TreeMap<WindRange, Map<PointOfSail, Float>>() {
{
@@ -120,15 +120,25 @@ public enum BoatClassType {
private String displayName;
private Map<CourseLayouts, Integer> possipleCourseLayoutsWithStandardTargetTime;
private Map<WindRange, Map<PointOfSail, Float>> boatSpeedTable;
private Integer startLineLengthInMeters;
private BoatClassType(String displayName, Map<CourseLayouts, Integer> possipleCourseLayoutsWithStandardTargetTime,
Map<WindRange, Map<PointOfSail, Float>> boatSpeedTable, Integer startLineLengthInMeters) {
this.displayName = displayName;
this.possipleCourseLayoutsWithStandardTargetTime = possipleCourseLayoutsWithStandardTargetTime;
this.boatSpeedTable = boatSpeedTable;
this.startLineLengthInMeters = startLineLengthInMeters;
}
public Map<WindRange, Map<PointOfSail, Float>> getBoatSpeedTable() {
return boatSpeedTable;
}
public void setBoatSpeedTable(Map<WindRange, Map<PointOfSail, Float>> boatSpeedTable) {
this.boatSpeedTable = boatSpeedTable;
}
@Override
public String toString() {
return displayName;
@@ -1,6 +1,23 @@
package com.sap.sailing.domain.coursedesign;
import java.util.Set;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
public interface CourseDesign {
Position getStartBoatPosition();
void setStartBoatPosition(Position startBoatPosition);
Double getWindSpeed();
void setWindSpeed(Double windSpeed);
Bearing getWindDirection();
void setWindDirection(Bearing windDirection);
PositionedMark getPinEnd();
public void setPinEnd(PositionedMark pinEnd);
Position getReferencePoint();
public void setReferencePoint(Position referencePoint);
void setCourseDesignSpecificMarks(Set<PositionedMark> courseDesignSpecificMarks);
Set<PositionedMark> getCourseDesignSpecificMarks();
}
@@ -1,8 +1,9 @@
package com.sap.sailing.domain.coursedesign;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
public interface CourseDesignFactory {
CourseDesign createCourseDesign(Position startBoatPosition, Double windSpeed, Integer windDirection, BoatClassType boatClass, CourseLayouts courseLayout, NumberOfRounds numberOfRounds, TargetTime targetTime);
CourseDesign createCourseDesign(Position startBoatPosition, Double windSpeed, Bearing windDirection, BoatClassType boatClass, CourseLayouts courseLayout, NumberOfRounds numberOfRounds, TargetTime targetTime);
}
@@ -0,0 +1,29 @@
package com.sap.sailing.domain.coursedesign;
import java.util.Set;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
public class TrapezoidCourseDesignFactoryImpl extends AbstractCourseDesignFactory {
@Override
public CourseDesign createCourseDesign(Position startBoatPosition, Double windSpeed, Bearing windDirection,
BoatClassType boatClass, CourseLayouts courseLayout, NumberOfRounds numberOfRounds, TargetTime targetTime) {
this.product = new TrapezoidCourseDesignImpl();
this.product.setStartBoatPosition(startBoatPosition);
this.product.setWindSpeed(windSpeed);
this.product.setWindDirection(windDirection);
setPinEnd(boatClass, startBoatPosition, windDirection);
return this.product;
}
@Override
protected Set<PositionedMark> computeDesignSpecificMarks(Position startBoatPosition, Double windSpeed,
Bearing windDirection, BoatClassType boatClass, CourseLayouts courseLayout, NumberOfRounds numberOfRounds,
TargetTime targetTime) {
// TODO Auto-generated method stub
return null;
}
}
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.coursedesign;
public class TrapezoidCourseDesignImpl extends AbstractCourseDesign {
}
@@ -9,7 +9,6 @@ public enum TrapezoidCourseLayouts implements CourseLayouts{
private String displayName;
private String shortName;
private Integer reachAngle;
private Class<? extends CourseDesignFactory> courseDesignFactoryClass;
public Integer getReachAngle() {
return reachAngle;
@@ -32,6 +31,6 @@ public enum TrapezoidCourseLayouts implements CourseLayouts{
@Override
public Class<? extends CourseDesignFactory> getCourseDesignFactoryClass() {
return this.courseDesignFactoryClass;
return TrapezoidCourseDesignFactoryImpl.class;
}
}
@@ -16,6 +16,10 @@ public class WindRange implements Comparable<WindRange>{
public Integer getUpperBound() {
return upperBound;
}
public boolean isInRange(Double windSpeed){
return (lowerBound <= windSpeed && windSpeed <= upperBound) ? true : false;
}
@Override
public int compareTo(WindRange o) {
@@ -1,20 +1,18 @@
package com.sap.sailing.domain.coursedesign;
public enum LeewardWindWardCourseLayouts implements CourseLayouts{
windWardLeewardWindward("Windward/Leeward with Windward finish", "W", WindWardLeewardCourseDesignFactoryImpl.class),
windWardLeewardLeeward("Windward/Leeward with Leeward finish", "L", WindWardLeewardCourseDesignFactoryImpl.class);
public enum WindWardLeeWardCourseLayouts implements CourseLayouts{
windWardLeewardWindward("Windward/Leeward with Windward finish", "W"),
windWardLeewardLeeward("Windward/Leeward with Leeward finish", "L");
private String displayName;
private String shortName;
private Class<? extends CourseDesignFactory> courseDesignFactoryClass;
@Override
public String getShortName() {
return shortName;
}
private LeewardWindWardCourseLayouts(String displayName, String shortName, Class<? extends CourseDesignFactory> courseDesignFactoryClass){
this.courseDesignFactoryClass = courseDesignFactoryClass;
private WindWardLeeWardCourseLayouts(String displayName, String shortName){
this.displayName = displayName;
this.shortName = shortName;
}
@@ -26,6 +24,6 @@ public enum LeewardWindWardCourseLayouts implements CourseLayouts{
@Override
public Class<? extends CourseDesignFactory> getCourseDesignFactoryClass() {
return this.courseDesignFactoryClass;
return WindWardLeeWardCourseDesignFactoryImpl.class;
}
}
@@ -1,22 +1,61 @@
package com.sap.sailing.domain.coursedesign;
import com.sap.sailing.domain.common.Position;
import java.text.DecimalFormat;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class WindWardLeewardCourseDesignFactoryImpl extends AbstractCourseDesignFactory {
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
public class WindWardLeeWardCourseDesignFactoryImpl extends AbstractCourseDesignFactory {
private final int LUV_BUOY1_ANGLE_TO_WIND = 0;
private final int LUV_BUOY2_ANGLE_TO_WIND = 100;
private final Distance LUV_BUOY1_TO_LUV_BUOY2_DISTANCE = new NauticalMileDistance(0.03);
@Override
public CourseDesign createCourseDesign(Position startBoatPosition, Double windSpeed, Bearing windDirection,
BoatClassType boatClass, CourseLayouts courseLayout, NumberOfRounds numberOfRounds, TargetTime targetTime) {
this.product = new WindWardLeeWardCourseDesignImpl();
this.initializeCourseDesign(startBoatPosition, windSpeed, windDirection, boatClass, courseLayout,
numberOfRounds, targetTime);
this.finalizeCourseDesign(startBoatPosition, windSpeed, windDirection, boatClass, courseLayout, numberOfRounds,
targetTime);
return this.product;
}
@Override
public CourseDesign createCourseDesign(Position startBoatPosition, Double windSpeed, Integer windDirection,
BoatClassType boatClass, CourseLayouts courseLayout, NumberOfRounds numberOfRounds, TargetTime targetTime) {
CourseDesign windWardLeewardCourseDesign = new WindwardLeewardCourseDesignImpl();
setPinEnd(windWardLeewardCourseDesign, boatClass, startBoatPosition, windDirection);
return windWardLeewardCourseDesign;
protected Set<PositionedMark> computeDesignSpecificMarks(Position startBoatPosition, Double windSpeed,
Bearing windDirection, BoatClassType boatClass, CourseLayouts courseLayout, NumberOfRounds numberOfRounds,
TargetTime targetTime) {
Set<PositionedMark> result = new HashSet<PositionedMark>();
Map<PointOfSail, Float> speedTable = null;
for (Entry<WindRange, Map<PointOfSail, Float>> windRangeToSpeedTable : boatClass.getBoatSpeedTable().entrySet()) {
if (windRangeToSpeedTable.getKey().isInRange(windSpeed)) {
speedTable = windRangeToSpeedTable.getValue();
break;
}
}
if(speedTable==null){
throw new IllegalArgumentException("There was no speed diagram for the given boat class and the given wind.");
}
double roundLength = targetTime.getTimeInMinutes()/numberOfRounds.getNumberOfRounds();
double legLength = roundLength/(speedTable.get(PointOfSail.Upwind)+speedTable.get(PointOfSail.Downwind));
Distance legDistance = new NauticalMileDistance(legLength);
Position luvBuoyPosition = getPositionForGivenPointDistanceAndBearing(
this.product.getReferencePoint(), legDistance,
windDirection.add(new DegreeBearingImpl(LUV_BUOY1_ANGLE_TO_WIND)));
DecimalFormat legLengthFormat = new DecimalFormat("0.00");
result.add(new PositionedMarkImpl("luv buoy 1 "+legLengthFormat.format(legDistance.getNauticalMiles()) , luvBuoyPosition));
result.add(new PositionedMarkImpl("luv buoy 2" , getPositionForGivenPointDistanceAndBearing(
luvBuoyPosition, LUV_BUOY1_TO_LUV_BUOY2_DISTANCE,
windDirection.add(new DegreeBearingImpl(LUV_BUOY2_ANGLE_TO_WIND)))));
return result;
}
private void setPinEnd(CourseDesign windWardLeewardCourseDesign, BoatClassType boatClass,
Position startBoatPosition, Integer windDirection) {
PositionedMark pinEnd = new PositionedMarkImpl("pinEnd", getPositionForGivenPointDistanceAndBearing(
startBoatPosition, boatClass.getStartLineLengthInMeters(), windDirection));
windWardLeewardCourseDesign.setPinEnd(pinEnd);
}
}
@@ -1,16 +1,8 @@
package com.sap.sailing.domain.coursedesign;
public class WindwardLeewardCourseDesignImpl implements CourseDesign{
PositionedMark pinEnd;
@Override
public PositionedMark getPinEnd() {
return pinEnd;
}
@Override
public void setPinEnd(PositionedMark pinEnd) {
this.pinEnd = pinEnd;
}
public class WindWardLeeWardCourseDesignImpl extends AbstractCourseDesign {
}
@@ -1,3 +1,2 @@
eclipse.preferences.version=1
encoding//src/com/sap/sailing/domain/base/impl/AbstractSpeedWithAbstractBearingImpl.java=UTF-8
encoding//src/com/sap/sailing/domain/tracking/impl/CompactGPSFixMovingImpl.java=UTF-8
@@ -11,7 +11,9 @@ import org.json.simple.JSONObject;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnInSeries;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.TimePoint;
@@ -72,27 +74,85 @@ public class LeaderboardGroupJsonGetServlet extends AbstractJsonHttpServlet {
jsonLeaderboard.put("scoringScheme", leaderboard.getScoringScheme().getType());
jsonLeaderboard.put("regattaName", regatta.getName());
JSONArray jsonSeriesEntries = new JSONArray();
jsonLeaderboard.put("series", jsonSeriesEntries);
for (Series series: regatta.getSeries()) {
JSONObject jsonSeries = new JSONObject();
jsonSeries.put("name", series.getName());
jsonSeries.put("isMedalSeries", series.isMedal());
JSONArray jsonFleetsEntries = new JSONArray();
jsonSeries.put("fleets", jsonFleetsEntries);
for(Fleet fleet: series.getFleets()) {
JSONObject jsonFleet = new JSONObject();
jsonFleet.put("name", fleet.getName());
jsonFleet.put("color", fleet.getColor() != null ? fleet.getColor().getAsHtml() : null);
jsonFleet.put("ordering", fleet.getOrdering());
jsonFleetsEntries.add(jsonFleet);
JSONArray jsonRacesEntries = new JSONArray();
jsonFleet.put("races", jsonRacesEntries);
for(RaceColumnInSeries raceColumn: series.getRaceColumns()) {
JSONObject jsonRaceColumn = new JSONObject();
jsonRaceColumn.put("name", raceColumn.getName());
jsonRaceColumn.put("isMedalRace" , raceColumn.isMedalRace());
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
if(trackedRace != null) {
jsonRaceColumn.put("isTracked", true);
jsonRaceColumn.put("trackedRaceName", trackedRace.getRace().getName());
} else {
jsonRaceColumn.put("isTracked", false);
jsonRaceColumn.put("trackedRaceName", null);
}
jsonRacesEntries.add(jsonRaceColumn);
}
jsonSeriesEntries.add(jsonSeries);
}
}
} else {
jsonLeaderboard.put("scoringScheme", leaderboard.getScoringScheme().getType());
jsonLeaderboard.put("regattaName", null);
JSONArray jsonRacesEntries = new JSONArray();
jsonLeaderboard.put("races", jsonRacesEntries);
for(RaceColumn raceColumn: leaderboard.getRaceColumns()) {
JSONObject jsonRaceColumn = new JSONObject();
jsonRaceColumn.put("name", raceColumn.getName());
jsonRaceColumn.put("isMedalRace" , raceColumn.isMedalRace());
JSONArray jsonSeriesEntries = new JSONArray();
jsonLeaderboard.put("series", jsonSeriesEntries);
// write a 'default' series to be conform with our common regatta structure
JSONObject jsonSeries = new JSONObject();
jsonSeriesEntries.add(jsonSeries);
jsonSeries.put("name", LeaderboardNameConstants.DEFAULT_SERIES_NAME);
jsonSeries.put("isMedalSeries", null);
JSONArray jsonFleetsEntries = new JSONArray();
jsonSeries.put("fleets", jsonFleetsEntries);
Fleet fleet = leaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME);
if(fleet != null) {
JSONObject jsonFleet = new JSONObject();
jsonFleet.put("name", fleet.getName());
jsonFleet.put("color", fleet.getColor() != null ? fleet.getColor().getAsHtml() : null);
jsonFleet.put("ordering", fleet.getOrdering());
jsonFleetsEntries.add(jsonFleet);
JSONArray jsonRacesEntries = new JSONArray();
jsonFleet.put("races", jsonRacesEntries);
for(RaceColumn raceColumn: leaderboard.getRaceColumns()) {
JSONObject jsonRaceColumn = new JSONObject();
jsonRaceColumn.put("name", raceColumn.getName());
jsonRaceColumn.put("isMedalRace" , raceColumn.isMedalRace());
Fleet fleet = leaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME);
if(fleet != null && raceColumn.getTrackedRace(fleet) != null) {
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
jsonRaceColumn.put("isTracked", true);
jsonRaceColumn.put("trackedRaceName", trackedRace.getRace().getName());
} else {
jsonRaceColumn.put("isTracked", false);
jsonRaceColumn.put("trackedRaceName", null);
if(trackedRace != null) {
jsonRaceColumn.put("isTracked", true);
jsonRaceColumn.put("trackedRaceName", trackedRace.getRace().getName());
} else {
jsonRaceColumn.put("isTracked", false);
jsonRaceColumn.put("trackedRaceName", null);
}
jsonRacesEntries.add(jsonRaceColumn);
}
jsonRacesEntries.add(jsonRaceColumn);
}
}
}
@@ -1,7 +1,7 @@
package com.sap.sailing.racecommittee.app.coursedesigner;
import com.google.android.gms.maps.model.LatLng;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.coursedesign.BoatClassType;
import com.sap.sailing.domain.coursedesign.CourseDesign;
import com.sap.sailing.domain.coursedesign.CourseLayouts;
@@ -9,19 +9,19 @@ import com.sap.sailing.domain.coursedesign.NumberOfRounds;
import com.sap.sailing.domain.coursedesign.TargetTime;
public class CourseDesignComputer {
private LatLng startBoatPosition;
private Position startBoatPosition;
private Double windSpeed;
private Integer windDirection;
private Bearing windDirection;
private BoatClassType boatClass;
private CourseLayouts courseLayout;
private NumberOfRounds numberOfRounds;
private TargetTime targetTime;
public LatLng getStartBoatPosition() {
public Position getStartBoatPosition() {
return startBoatPosition;
}
public CourseDesignComputer setStartBoatPosition(LatLng startBoatPosition) {
public CourseDesignComputer setStartBoatPosition(Position startBoatPosition) {
this.startBoatPosition = startBoatPosition;
return this;
}
@@ -35,11 +35,11 @@ public class CourseDesignComputer {
return this;
}
public Integer getWindDirection() {
public Bearing getWindDirection() {
return windDirection;
}
public CourseDesignComputer setWindDirection(Integer windDirection) {
public CourseDesignComputer setWindDirection(Bearing windDirection) {
this.windDirection = windDirection;
return this;
}
@@ -86,7 +86,7 @@ public class CourseDesignComputer {
&& courseLayout != null && numberOfRounds != null && targetTime != null) {
try {
computedCourseDesign = courseLayout.getCourseDesignFactoryClass().newInstance()
.createCourseDesign(new DegreePosition(startBoatPosition.latitude, startBoatPosition.longitude), windSpeed, windDirection, boatClass, courseLayout, numberOfRounds, targetTime);
.createCourseDesign(startBoatPosition, windSpeed, windDirection, boatClass, courseLayout, numberOfRounds, targetTime);
} catch (InstantiationException e) {
e.printStackTrace();
@@ -3,7 +3,6 @@ package com.sap.sailing.racecommittee.app.data;
import java.io.Serializable;
import java.util.Collection;
import com.google.android.gms.maps.model.LatLng;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.EventBase;
@@ -36,11 +35,4 @@ public interface DataStore {
public CourseBase getLastPublishedCourseDesign();
public void setLastPublishedCourseDesign(CourseBase courseData);
public LatLng getLastWindPosition();
public void setLastWindPosition(LatLng lastWindPosition);
public Integer getLastWindDirection();
public void setLastWindDirection(Integer lastWindDirection);
public Double getLastWindSpeed();
public void setLastWindSpeed(Double lastWindSpeed);
}
@@ -4,7 +4,6 @@ import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import com.google.android.gms.maps.model.LatLng;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.EventBase;
@@ -19,9 +18,6 @@ public enum InMemoryDataStore implements DataStore {
private HashMap<Serializable, ManagedRace> managedRaceById;
private HashMap<Serializable, Mark> marksById;
private CourseBase courseData;
private Double lastWindSpeed;
private Integer lastWindDirection;
private LatLng lastWindPosition;
private InMemoryDataStore() {
reset();
@@ -166,37 +162,4 @@ public enum InMemoryDataStore implements DataStore {
public void setLastPublishedCourseDesign(CourseBase courseData) {
this.courseData = courseData;
}
/*
* * * * * * *
* Wind *
* * * * * * *
*/
@Override
public Integer getLastWindDirection() {
return lastWindDirection;
}
@Override
public void setLastWindDirection(Integer lastWindDirection) {
this.lastWindDirection = lastWindDirection;
}
@Override
public Double getLastWindSpeed() {
return lastWindSpeed;
}
@Override
public void setLastWindSpeed(Double lastWindSpeed) {
this.lastWindSpeed = lastWindSpeed;
}
@Override
public void setLastWindPosition(LatLng lastWindPosition) {
this.lastWindPosition = lastWindPosition;
}
@Override
public LatLng getLastWindPosition() {
return this.lastWindPosition;
}
}
@@ -9,6 +9,7 @@ import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.common.racelog.StartProcedureType;
import com.sap.sailing.domain.coursedesign.CourseDesign;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.racecommittee.app.domain.startprocedure.StartProcedure;
@@ -15,6 +15,7 @@ import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.common.racelog.StartProcedureType;
import com.sap.sailing.domain.coursedesign.CourseDesign;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogEventFactory;
@@ -160,7 +161,8 @@ public class RaceStateImpl implements RaceState, RaceLogChangedListener {
public CourseBase getCourseDesign() {
return lastCourseDesignFinder.analyze();
}
@Override
public void setCourseDesign(CourseBase newCourseData) {
TimePoint eventTime = MillisecondsTimePoint.now();
@@ -170,7 +172,7 @@ public class RaceStateImpl implements RaceState, RaceLogChangedListener {
fireCourseDesignChanged();
}
@Override
public void onRaceAborted(TimePoint eventTime) {
/*RaceLogEvent abortEvent = RaceLogEventFactory.INSTANCE.createRaceStatusEvent(eventTime, raceLog.getCurrentPassId(), RaceLogRaceStatus.UNSCHEDULED);
@@ -5,6 +5,7 @@ import java.text.DecimalFormat;
import android.content.Context;
import android.content.Intent;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
@@ -64,10 +65,13 @@ public class WindActivity extends BaseActivity implements CompassDirectionListen
windSpeedSeekBar = (SeekBar) findViewById(R.id.seekbar_wind_speed);
sendButton = (Button) findViewById(R.id.btn_wind_send);
currentLocation = null;
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, FIVE_SEC, EVERY_POSITION_CHANGE, this);
Criteria crit = new Criteria();
crit.setAccuracy(Criteria.ACCURACY_FINE);
String provider = locationManager.getBestProvider(crit, true);
onLocationChanged(locationManager.getLastKnownLocation(provider));
windSpeedSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
@@ -86,7 +90,6 @@ public class WindActivity extends BaseActivity implements CompassDirectionListen
}
});
sendButton.setEnabled(false);
sendButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
@@ -120,7 +123,7 @@ public class WindActivity extends BaseActivity implements CompassDirectionListen
});
speedFormat = new DecimalFormat("##.0");
speedFormat = new DecimalFormat("#0.0");
bearingFormat = new DecimalFormat("###");
double enteredWindSpeed = AppPreferences.getWindSpeed(getBaseContext());
@@ -158,7 +161,6 @@ public class WindActivity extends BaseActivity implements CompassDirectionListen
currentLocation = location;
latitudeEditText.setText(String.valueOf(location.getLatitude()));
longitudeEditText.setText(String.valueOf(location.getLongitude()));
sendButton.setEnabled(true);
}
@Override
@@ -188,7 +190,7 @@ public class WindActivity extends BaseActivity implements CompassDirectionListen
}
Position currentPosition = new DegreePosition(currentLocation.getLatitude(), currentLocation.getLongitude());
double windSpeed = Double.valueOf(windSpeedEditText.getText().toString());
double windSpeed = Double.valueOf(windSpeedEditText.getText().toString().replace(",", "."));
double windBearing = Double.valueOf(windBearingEditText.getText().toString());
Bearing bearing = new DegreeBearingImpl(windBearing);
SpeedWithBearing speedBearing = new KnotSpeedWithBearingImpl(windSpeed, bearing);
@@ -27,17 +27,18 @@ import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.coursedesign.BoatClassType;
import com.sap.sailing.domain.coursedesign.CourseDesign;
import com.sap.sailing.domain.coursedesign.CourseLayouts;
import com.sap.sailing.domain.coursedesign.NumberOfRounds;
import com.sap.sailing.domain.coursedesign.PositionedMark;
import com.sap.sailing.domain.coursedesign.TargetTime;
import com.sap.sailing.domain.racelog.analyzing.impl.LastWindFixFinder;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.racecommittee.app.AppConstants;
import com.sap.sailing.racecommittee.app.R;
import com.sap.sailing.racecommittee.app.coursedesigner.CourseDesignComputer;
import com.sap.sailing.racecommittee.app.data.DataStore;
import com.sap.sailing.racecommittee.app.data.InMemoryDataStore;
import com.sap.sailing.racecommittee.app.ui.activities.WindActivity;
public class ClassicCourseDesignDialogFragment extends RaceDialogFragment {
@@ -70,7 +71,8 @@ public class ClassicCourseDesignDialogFragment extends RaceDialogFragment {
public ClassicCourseDesignDialogFragment() {
super();
// handle bug "Dark overlay of MapFragment in Activity with Dialog theme" - https://code.google.com/p/gmaps-api-issues/issues/detail?id=4865
// handle bug "Dark overlay of MapFragment in Activity with Dialog theme" -
// https://code.google.com/p/gmaps-api-issues/issues/detail?id=4865
this.setStyle(DialogFragment.STYLE_NORMAL, R.style.Theme_DimDisabledDialog);
}
@@ -94,7 +96,7 @@ public class ClassicCourseDesignDialogFragment extends RaceDialogFragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getDialog().setTitle(getActivity().getText(R.string.course_design_dialog_title));
setUpMapIfNeeded(getView());
publishButton = (Button) getView().findViewById(R.id.publishCourseDesignButton);
publishButton.setOnClickListener(new OnClickListener() {
@@ -114,7 +116,11 @@ public class ClassicCourseDesignDialogFragment extends RaceDialogFragment {
}
});
courseDesignComputer = new CourseDesignComputer().setBoatClass(selectedBoatClass)
.setCourseLayout(selectedCourseLayout).setNumberOfRounds(selectedNumberOfRounds)
.setTargetTime(selectedTargetTime);
spinnerBoatClass = (Spinner) getView().findViewById(R.id.classic_course_designer_boat_class);
setupBoatClassSpinner();
spinnerCourseLayout = (Spinner) getView().findViewById(R.id.classic_course_designer_course_layout);
@@ -124,12 +130,17 @@ public class ClassicCourseDesignDialogFragment extends RaceDialogFragment {
spinnerTargetTime = (Spinner) getView().findViewById(R.id.classic_course_designer_target_time);
setupTargetTimeSpinner();
DataStore ds = InMemoryDataStore.INSTANCE;
courseDesignComputer = new CourseDesignComputer().setStartBoatPosition(ds.getLastWindPosition())
.setWindDirection(ds.getLastWindDirection()).setWindSpeed(ds.getLastWindSpeed())
.setBoatClass(selectedBoatClass).setCourseLayout(selectedCourseLayout)
.setNumberOfRounds(selectedNumberOfRounds).setTargetTime(selectedTargetTime);
setUpMapIfNeeded(getView());
LastWindFixFinder lastWindFixFinder = new LastWindFixFinder(getRace().getRaceLog());
Wind lastWind = lastWindFixFinder.analyze();
if (lastWind != null) {
courseDesignComputer.setWindSpeed(lastWind.getKnots());
courseDesignComputer.setWindDirection(lastWind.getBearing());
courseDesignComputer.setStartBoatPosition(lastWind.getPosition());
recomputeCourseDesign();
} else {
Toast.makeText(getActivity(), "Set the wind information, please!", Toast.LENGTH_LONG).show();
}
}
@Override
@@ -141,33 +152,26 @@ public class ClassicCourseDesignDialogFragment extends RaceDialogFragment {
Wind windFix = (Wind) data.getSerializableExtra(AppConstants.EXTRAS_WIND_FIX);
onWindEntered(windFix);
}
DataStore ds = InMemoryDataStore.INSTANCE;
courseDesignComputer = courseDesignComputer.setStartBoatPosition(ds.getLastWindPosition())
.setWindDirection(ds.getLastWindDirection()).setWindSpeed(ds.getLastWindSpeed());
recomputeCourseDesign();
}
}
}
private void onWindEntered(Wind windFix) {
getRace().getState().setWindFix(windFix);
//TODO integrate the wind fix into the course designer
courseDesignComputer.setStartBoatPosition(windFix.getPosition());
courseDesignComputer.setWindDirection(windFix.getBearing());
courseDesignComputer.setWindSpeed(windFix.getKnots());
recomputeCourseDesign();
}
private CourseDesign recomputeCourseDesign() {
CourseDesign courseDesign = null;
try {
courseDesign = courseDesignComputer.compute();
} catch (IllegalStateException ise) {
Toast.makeText(getActivity(), ise.getMessage(), Toast.LENGTH_LONG).show();
}
/*
* DataStore ds = InMemoryDataStore.INSTANCE; Toast.makeText( getActivity(), "" + ds.getLastWindPosition() +
* ds.getLastWindDirection() + ds.getLastWindSpeed() + selectedBoatClass + selectedCourseLayout +
* selectedNumberOfRounds + selectedTargetTime, Toast.LENGTH_LONG).show()
*/;
return courseDesign;
private void recomputeCourseDesign() {
try {
drawMap(courseDesignComputer.compute());
} catch (IllegalStateException ise) {
Toast.makeText(getActivity(), ise.getMessage(), Toast.LENGTH_LONG).show();
} catch (IllegalArgumentException iae) {
Toast.makeText(getActivity(), iae.getMessage(), Toast.LENGTH_LONG).show();
}
}
private void setupBoatClassSpinner() {
@@ -178,9 +182,17 @@ public class ClassicCourseDesignDialogFragment extends RaceDialogFragment {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
selectedBoatClass = (BoatClassType) adapterView.getItemAtPosition(position);
//update possible course layouts
courseLayoutAdapter.clear();
courseLayoutAdapter.addAll(selectedBoatClass.getPossibleCourseLayoutsWithTargetTime().keySet());
courseLayoutAdapter.notifyDataSetChanged();
selectedCourseLayout = (CourseLayouts) selectedBoatClass
.getPossibleCourseLayoutsWithTargetTime().keySet().toArray().clone()[0];
spinnerCourseLayout.setSelection(courseLayoutAdapter.getPosition(selectedCourseLayout));
courseDesignComputer.setBoatClass(selectedBoatClass);
if (selectedBoatClass.getPossibleCourseLayoutsWithTargetTime().keySet().contains(selectedCourseLayout)) {
recomputeCourseDesign();
}
@@ -203,6 +215,7 @@ public class ClassicCourseDesignDialogFragment extends RaceDialogFragment {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
selectedCourseLayout = (CourseLayouts) adapterView.getItemAtPosition(position);
courseDesignComputer.setCourseLayout(selectedCourseLayout);
recomputeCourseDesign();
}
@@ -222,6 +235,7 @@ public class ClassicCourseDesignDialogFragment extends RaceDialogFragment {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
selectedNumberOfRounds = (NumberOfRounds) adapterView.getItemAtPosition(position);
courseDesignComputer.setNumberOfRounds(selectedNumberOfRounds);
recomputeCourseDesign();
}
@@ -241,6 +255,7 @@ public class ClassicCourseDesignDialogFragment extends RaceDialogFragment {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
selectedTargetTime = (TargetTime) adapterView.getItemAtPosition(position);
courseDesignComputer.setTargetTime(selectedTargetTime);
recomputeCourseDesign();
}
@@ -256,7 +271,6 @@ public class ClassicCourseDesignDialogFragment extends RaceDialogFragment {
public void onResume() {
super.onResume();
mMapView.onResume();
setUpMap();
}
@Override
@@ -273,43 +287,48 @@ public class ClassicCourseDesignDialogFragment extends RaceDialogFragment {
private void setUpMapIfNeeded(View inflatedView) {
if (courseAreaMap == null) {
courseAreaMap = ((MapView) inflatedView.findViewById(R.id.mapView)).getMap();
if (courseAreaMap != null) {
setUpMap();
}
courseAreaMap.getUiSettings().setRotateGesturesEnabled(false);
}
}
private void setUpMap() {
private void drawMap(CourseDesign courseDesign) {
courseAreaMap.clear();
DataStore ds = InMemoryDataStore.INSTANCE;
if (ds.getLastWindPosition() != null && ds.getLastWindDirection() != null && ds.getLastWindSpeed() != null) {
courseAreaMap.animateCamera(CameraUpdateFactory.newLatLngZoom(ds.getLastWindPosition(), 17.0f));
courseAreaMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
position2LatLng(courseDesign.getStartBoatPosition()), 16.0f));
Bitmap bmpOriginal = BitmapFactory.decodeResource(this.getResources(), R.drawable.boat);
Bitmap bmResult = Bitmap.createBitmap(bmpOriginal.getHeight(), bmpOriginal.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas tempCanvas = new Canvas(bmResult);
tempCanvas.rotate(ds.getLastWindDirection(), bmpOriginal.getWidth() / 2, bmpOriginal.getHeight() / 2);
tempCanvas.drawBitmap(bmpOriginal, 0, 0, null);
Bitmap bmpOriginal = BitmapFactory.decodeResource(this.getResources(), R.drawable.boat);
Bitmap bmResult = Bitmap
.createBitmap(bmpOriginal.getHeight(), bmpOriginal.getHeight(), Bitmap.Config.ARGB_8888);
Canvas tempCanvas = new Canvas(bmResult);
tempCanvas.rotate((float) courseDesign.getWindDirection().getDegrees(), bmpOriginal.getWidth() / 2,
bmpOriginal.getHeight() / 2);
tempCanvas.drawBitmap(bmpOriginal, 0, 0, null);
courseAreaMap.addMarker(new MarkerOptions()
.position(ds.getLastWindPosition())
.icon(BitmapDescriptorFactory.fromBitmap(bmResult))
.draggable(false)
.title(ds.getLastWindPosition() + ", " + ds.getLastWindSpeed() + "kn, " + ds.getLastWindDirection()
+ "°"));
}
CourseDesign courseDesign = recomputeCourseDesign();
if (courseDesign != null) {
LatLng pinEndPosition = new LatLng(courseDesign.getPinEnd().getPosition().getLatDeg(), courseDesign
.getPinEnd().getPosition().getLngDeg());
courseAreaMap.addMarker(new MarkerOptions().position(pinEndPosition)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.buoy_red)).draggable(false)
.title(courseDesign.getPinEnd().getName()));
courseAreaMap.addMarker(new MarkerOptions()
.position(position2LatLng(courseDesign.getStartBoatPosition()))
.icon(BitmapDescriptorFactory.fromBitmap(bmResult))
.draggable(false)
.title(position2LatLng(courseDesign.getStartBoatPosition()) + ", " + courseDesign.getWindSpeed()
+ "kn, " + courseDesign.getWindDirection()));
LatLng pinEndPosition = new LatLng(courseDesign.getPinEnd().getPosition().getLatDeg(), courseDesign.getPinEnd()
.getPosition().getLngDeg());
courseAreaMap.addMarker(new MarkerOptions().position(pinEndPosition)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.buoy_red)).draggable(false)
.title(courseDesign.getPinEnd().getName()));
for(PositionedMark mark : courseDesign.getCourseDesignSpecificMarks()){
LatLng markPosition = new LatLng(mark.getPosition().getLatDeg(), mark.getPosition().getLngDeg());
courseAreaMap.addMarker(new MarkerOptions().position(markPosition)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.buoy_black_cone)).draggable(false)
.title(mark.getName()));
}
}
private LatLng position2LatLng(Position p) {
return new LatLng(p.getLatDeg(), p.getLngDeg());
}
@Override
public void onPause() {
super.onPause();