Merge remote-tracking branch 'origin/master' into bug2610_lifecycle

Conflicts:
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java
This commit is contained in:
fmittag committed 2016-02-18 15:14:20 +01:00
commit 71d1338ed7
79 files changed
+2006 -2353

No files matched your search

+1
View File
@@ -26,6 +26,7 @@ SAP is at the center of today’s technology revolution, developing innovations
* [[Polars|wiki/Polars]]
* Production Environment
* [[Amazon EC2|wiki/amazon-ec2]]
* [[SSL / HTTPS Support|wiki/ssl-support]]
* [[EC2 Backup Strategy|wiki/amazon-ec2-backup-strategy]]
* [[TracTrac|wiki/tractrac-lifecycle]]
* [[Analytics on a stick|wiki/analytics-on-a-stick]]
@@ -70,7 +70,7 @@ public abstract class AbstractTracTracLiveTest extends StoredTrackBasedTest {
public void setUp() throws MalformedURLException, IOException, InterruptedException, URISyntaxException, SubscriberInitializationException, ParseException, CreateModelException {
final String eventID = "event_20110505_SailingTea";
final String raceID = "bd8c778e-7c65-11e0-8236-406186cbf87c";
setUp(getParamURL(eventID, raceID), getLiveURI(), getStoredURI());
setUp(getParamURL(eventID, raceID), /* liveURI */ null, /* storedURI */ null);
}
public static URI getStoredURI() throws URISyntaxException {
@@ -82,8 +82,7 @@ public abstract class AbstractTracTracLiveTest extends StoredTrackBasedTest {
}
public static URL getParamURL(final String eventID, final String raceID) throws MalformedURLException {
return new URL("http://" + TracTracConnectionConstants.HOST_NAME + "/events/event_20110505_SailingTea/clientparams.php?event="+eventID+
"&race="+raceID);
return new URL("http://" + TracTracConnectionConstants.HOST_NAME + "/events/"+eventID+"/"+raceID+".txt");
}
protected IEventSubscriber getEventSubscriber() {
@@ -99,11 +98,16 @@ public abstract class AbstractTracTracLiveTest extends StoredTrackBasedTest {
final IRace race = ModelLocator.getEventFactory().createRace(new URI(paramUrl.toString()));
this.race = race;
ISubscriberFactory subscriberFactory = SubscriptionLocator.getSusbcriberFactory();
eventSubscriber = subscriberFactory.createEventSubscriber(race.getEvent(), liveUri, storedUri);
raceSubscriber = subscriberFactory.createRaceSubscriber(race, liveUri, storedUri);
if (storedUri == null) {
eventSubscriber = subscriberFactory.createEventSubscriber(race.getEvent());
raceSubscriber = subscriberFactory.createRaceSubscriber(race);
} else {
eventSubscriber = subscriberFactory.createEventSubscriber(race.getEvent(), liveUri, storedUri);
raceSubscriber = subscriberFactory.createRaceSubscriber(race, liveUri, storedUri);
}
assertNotNull(race);
// Initialize data controller using live and stored data sources
if (storedUri.toString().startsWith("file:")) {
if (storedUri != null && storedUri.toString().startsWith("file:")) {
try {
URI oldStoredUri = storedUri;
storedUri = new URI(storedUri.toString().replaceFirst("file:/([^/])", "file:////$1"));
@@ -6,6 +6,7 @@ import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.util.List;
@@ -27,6 +28,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tractracadapter.ReceiverType;
import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.tractrac.model.lib.api.event.CreateModelException;
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
@@ -43,7 +45,10 @@ public class ConcurrencyTest extends OnlineTracTracBasedTest {
super.setUp();
// load Race8 of RC44 Cup in Sweden
super.setUp("event_20110815_RCSwedenCu",
/* raceId */ "1cdf8398-cafe-11e0-8c8e-406186cbf87c", new ReceiverType[] { ReceiverType.MARKPASSINGS,
/* raceId */ "event_20110815_RCSwedenCu-Race8",
/* liveUri */ null,
/* storedUri */ tractracTunnel ? new URI("tcp://"+tractracTunnelHost+":"+TracTracConnectionConstants.PORT_TUNNEL_STORED) : new URI("tcp://" + TracTracConnectionConstants.HOST_NAME + ":" + TracTracConnectionConstants.PORT_STORED),
new ReceiverType[] { ReceiverType.MARKPASSINGS,
ReceiverType.RACECOURSE, ReceiverType.RAWPOSITIONS });
getTrackedRace().recordWind(
new WindImpl(/* position */null, MillisecondsTimePoint.now(), new KnotSpeedWithBearingImpl(12,
@@ -53,7 +53,9 @@ public class ManeuverDetectionOnMdM2011SemifinalTest extends OnlineTracTracBased
public void setUp() throws MalformedURLException, IOException, InterruptedException, URISyntaxException, ParseException, SubscriberInitializationException, CreateModelException {
super.setUp();
super.setUp("event_20110505_SailingTea", // Semifinale
/* raceId */ "01ea3604-02ef-11e1-9efc-406186cbf87c", new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.RACECOURSE, ReceiverType.RAWPOSITIONS });
/* raceId */ "01ea3604-02ef-11e1-9efc-406186cbf87c",
/* liveUri */ null, /* storedUri */ null,
new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.RACECOURSE, ReceiverType.RAWPOSITIONS });
TimePoint epoch = new MillisecondsTimePoint(0l);
TimePoint now = MillisecondsTimePoint.now();
Map<String, Position> markPositions = new HashMap<String, Position>();
@@ -97,6 +97,12 @@ public abstract class OnlineTracTracBasedTest extends AbstractTracTracLiveTest {
finishSetUp(receiverTypes);
}
protected void setUp(String regattaName, String raceId, URI liveUri, URI storedUri, ReceiverType... receiverTypes)
throws MalformedURLException, IOException, InterruptedException, URISyntaxException,
SubscriberInitializationException, CreateModelException {
setUpWithoutLaunchingController(regattaName, raceId, liveUri, storedUri);
finishSetUp(receiverTypes);
}
private void finishSetUp(ReceiverType... receiverTypes) throws InterruptedException {
assertEquals(getExpectedEventName(), getTracTracEvent().getName());
@@ -208,9 +214,16 @@ public abstract class OnlineTracTracBasedTest extends AbstractTracTracLiveTest {
protected void setUpWithoutLaunchingController(String regattaName, String raceId) throws FileNotFoundException, MalformedURLException,
URISyntaxException, SubscriberInitializationException, CreateModelException {
final URL paramUrl = new URL("http://" + TracTracConnectionConstants.HOST_NAME + "/events/"+regattaName+"/clientparams.php?event="+regattaName+"&race="+raceId);
final URI liveUri = tractracTunnel ? new URI("tcp://"+tractracTunnelHost+":"+TracTracConnectionConstants.PORT_TUNNEL_LIVE) : new URI("tcp://" + TracTracConnectionConstants.HOST_NAME + ":" + TracTracConnectionConstants.PORT_LIVE);
final URI storedUri = tractracTunnel ? new URI("tcp://"+tractracTunnelHost+":"+TracTracConnectionConstants.PORT_TUNNEL_STORED) : new URI("tcp://" + TracTracConnectionConstants.HOST_NAME + ":" + TracTracConnectionConstants.PORT_STORED);
setUpWithoutLaunchingController(regattaName, raceId, liveUri, storedUri);
}
protected void setUpWithoutLaunchingController(String regattaName, String raceId, final URI liveUri,
final URI storedUri) throws MalformedURLException, FileNotFoundException, URISyntaxException,
SubscriberInitializationException, CreateModelException {
final URL paramUrl = new URL("http://" + TracTracConnectionConstants.HOST_NAME + "/events/"+regattaName+"/"+raceId+".txt");
setUpWithoutLaunchingController(paramUrl, liveUri, storedUri);
}
@@ -99,12 +99,15 @@ public class ParseTracTracJSONServiceOutputTest {
// replay races may have an empty / null live URI
assertTrue(race.getLiveURI() == null || new URI("tcp://" + TracTracConnectionConstants.HOST_NAME + ":4400").equals(race.getLiveURI()));
// and they point to stored data in the form of a binary ".mtb" file
String expectedSuffix = ".mtb";
final String expectedSuffix = ".mtb";
assertTrue(race.getStoredURI().toString().endsWith(expectedSuffix) ||
new URI("tcp://" + TracTracConnectionConstants.HOST_NAME + ":4401").equals(race.getStoredURI()));
} else {
assertEquals(new URI("tcp://" + TracTracConnectionConstants.HOST_NAME + ":4400"), race.getLiveURI());
assertEquals(new URI("tcp://" + TracTracConnectionConstants.HOST_NAME + ":4401"), race.getStoredURI());
if (!"HIDDEN".equals(race.getRaceStatus())) {
// hidden races may have a null live URI and perhaps have already been converted to an .mtb, so only assert if not a HIDDEN race
assertEquals(new URI("tcp://" + TracTracConnectionConstants.HOST_NAME + ":4400"), race.getLiveURI());
assertEquals(new URI("tcp://" + TracTracConnectionConstants.HOST_NAME + ":4401"), race.getStoredURI());
}
}
}
}
@@ -1,730 +0,0 @@
package com.sap.sailing.domain.test.markpassing;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableSet;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.test.OnlineTracTracBasedTest;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tractracadapter.ReceiverType;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.tractrac.model.lib.api.event.CreateModelException;
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
/**
* Tests the mark passing algorithm by using the data provided by an {@link OnlineTracTracBasedTest}. Subclasses may
* implement different mark passing detection algorithms.
*
* @author Martin Hanysz
*
*/
public abstract class MartinAbstractMarkPassingTest extends OnlineTracTracBasedTest {
/**
* How many milliseconds may the given and computed mark passings drift apart before being counted as a miss.
*/
private static final int TIMEDELTA_TOLERANCE = 10000;
/**
* Reload the test data from the web?
*/
private boolean forceReload = false;
public MartinAbstractMarkPassingTest() throws MalformedURLException, URISyntaxException {
super();
}
public void setUp() throws IOException, InterruptedException, URISyntaxException, ParseException, SubscriberInitializationException, CreateModelException {
super.setUp();
String raceID = "357c700a-9d9a-11e0-85be-406186cbf87c";
if (!loadData(raceID) && !forceReload) {
System.out.println("Downloading new data from the web.");
this.setUp("event_20110609_KielerWoch",
/* raceId */raceID, new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS,
ReceiverType.RACECOURSE, ReceiverType.RAWPOSITIONS });
OnlineTracTracBasedTest.fixApproximateMarkPositionsForWindReadOut(getTrackedRace(),
new MillisecondsTimePoint(new GregorianCalendar(2011, 05, 23).getTime()));
getTrackedRace().recordWind(
new WindImpl(/* position */null, MillisecondsTimePoint.now(), new KnotSpeedWithBearingImpl(12,
new DegreeBearingImpl(65))), new WindSourceImpl(WindSourceType.WEB));
saveData();
}
}
/**
* Loads stored data for the given raceID or returns false if no data is present.
*
* @param raceID
* - ID of the race to load from disk
* @return true if data was loaded, false if not
*/
private boolean loadData(String raceID) {
String path = null;
File file = new File("resources/");
if (file.exists() && file.isDirectory()) {
for (String fileName : file.list()) {
if (fileName.endsWith(".data") && fileName.contains(raceID)) {
path = "resources/" + fileName;
break;
}
}
}
if (path == null)
return false;
FileInputStream fs = null;
ObjectInputStream os = null;
Object obj = null;
try {
System.out.print("Loading cached data for raceID " + raceID + "...");
fs = new FileInputStream(path);
os = new ObjectInputStream(fs);
obj = os.readObject();
System.out.println("done!");
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
return false;
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
if (fs != null) {
try {
fs.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
}
if (obj != null && obj instanceof DynamicTrackedRaceImpl) {
setTrackedRace((DynamicTrackedRaceImpl) obj);
setRace(getTrackedRace().getRace());
return true;
}
return false;
}
/**
* Saves current result of getTrackedRace to disk for future reuse.
*/
private void saveData() {
DynamicTrackedRace trackedRace = getTrackedRace();
String racePath = "resources/" + trackedRace.getRace().getId() + ".data";
FileOutputStream fs = null;
ObjectOutputStream os = null;
try {
System.out.println("Caching data for raceID " + trackedRace.getRace().getId());
File f = new File(racePath);
f.createNewFile();
fs = new FileOutputStream(f);
os = new ObjectOutputStream(fs);
os.writeObject(trackedRace);
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
return;
}
}
if (fs != null) {
try {
fs.close();
} catch (IOException e) {
e.printStackTrace();
return;
}
}
}
}
public void testMarkPassings() {
List<MarkPassing> markPassings = computeAllMarkPassings();
// compare computed mark passings to given ones
int overallMisses = compareCalculatedAndGivenPassings(markPassings, true);
assertEquals("Calculation returned less mark passings than the test data contains", 0, overallMisses);
}
/**
* This method starts the actual mark passing detection algorithm by handing a sequence of {@link GPSFix}es of each
* {@link Competitor} into the {@link MartinAbstractMarkPassingTest#computeMarkPassings(Competitor, GPSFixMoving)}
* method. For every competitor, sequences of mark passings for each waypoint of the course can be calculated. One
* sequence may contain multiple possible mark passings for the same waypoint that may represent the same passing of
* that specific waypoint. The list of sequences returned in the result should contain one sequence for each time
* the waypoint has to be passed to complete the course.
*
* @return a {@link Map} from each {@link Competitor} to a {@link Map} from each {@link Waypoint} to a {@link List}
* of mark passing sequences
*/
protected List<MarkPassing> computeAllMarkPassings() {
ArrayList<MarkPassing> markPassings = new ArrayList<MarkPassing>();
// To imitate GPSFixes that are received over time, we hand the GPSFixes to the detection algorithm in
// chronological order
HashMap<Competitor, Iterator<GPSFixMoving>> fixesIterators = new HashMap<Competitor, Iterator<GPSFixMoving>>();
HashMap<Competitor, GPSFixMoving> nextFixes = new HashMap<Competitor, GPSFixMoving>();
// get all the GPSFix iterators
for (Competitor competitor : getRace().getCompetitors()) {
try {
getTrackedRace().getTrack(competitor).lockForRead();
// from the start of tracking or "the dawn of time"
TimePoint start = getTrackedRace().getStartOfTracking() != null ? getTrackedRace().getStartOfTracking()
: new MillisecondsTimePoint(0);
fixesIterators.put(competitor, getTrackedRace().getTrack(competitor).getRawFixesIterator(start, true));
} finally {
getTrackedRace().getTrack(competitor).unlockAfterRead();
}
}
// initially fill the next fix of each competitor to hand into the mark passing algorithm
for (Entry<Competitor, Iterator<GPSFixMoving>> entry : fixesIterators.entrySet()) {
if (entry.getValue().hasNext())
nextFixes.put(entry.getKey(), entry.getValue().next());
}
while (!nextFixes.isEmpty()) {
// Find the chronologically first unhandled fix.
Entry<Competitor, GPSFixMoving> firstUnhandledFix = null;
for (Entry<Competitor, GPSFixMoving> entry : nextFixes.entrySet()) {
if (firstUnhandledFix == null
|| entry.getValue().getTimePoint().compareTo(firstUnhandledFix.getValue().getTimePoint()) < 0) {
firstUnhandledFix = entry;
}
}
if (firstUnhandledFix != null) {
// compute a possible mark passing
MarkPassing computedPassing = computeMarkPassings(firstUnhandledFix.getKey(),
firstUnhandledFix.getValue());
// replace the fix we just handled by the next fix of the competitor's track
if (fixesIterators.get(firstUnhandledFix.getKey()).hasNext()) {
nextFixes.put(firstUnhandledFix.getKey(), fixesIterators.get(firstUnhandledFix.getKey()).next());
} else {
// If no further fixes can be obtained from the iterator, remove the competitor from the nextFixes
// map.
// This ensures the while loop eventually terminates after all fixes have been analyzed.
nextFixes.remove(firstUnhandledFix.getKey());
}
firstUnhandledFix = null;
if (computedPassing != null) {
// A new mark passing has been detected.
// Find out if it overwrites a previous mark passing or is a new one.
MarkPassing latestPassingOfCompetitor = null;
int index = -1;
for (int i = 0; i < markPassings.size(); i++) {
MarkPassing p = markPassings.get(i);
if (p.getCompetitor().equals(computedPassing.getCompetitor())) {
latestPassingOfCompetitor = p;
index = i;
}
}
if (latestPassingOfCompetitor != null
&& latestPassingOfCompetitor.getWaypoint().equals(computedPassing.getWaypoint())) {
// Latest passing of the competitor was at the same waypoint -> replace it with the new one
markPassings.set(index, computedPassing);
} else {
markPassings.add(computedPassing);
}
}
}
}
return markPassings;
}
/**
* This method starts the actual mark passing detection algorithm. It is supposed to analyze if the given
* {@link Competitor} passed a {@link Mark} at or before the given {@link GPSFixMoving}. If a {@link MarkPassing}
* for a previously passed {@link Waypoint} is detected, it must only return a non-null result, if the returned
* {@link MarkPassing} should overwrite the previously returned one.
*
* @param competitor
* - the {@link Competitor} the given {@link GPSFixMoving} belongs to
* @param fix
* - the {@link GPSFixMoving} representing the latest known position of the given {@link Competitor}
* @return a {@link List} of mark passings
*/
abstract MarkPassing computeMarkPassings(Competitor competitor, GPSFixMoving fix);
/**
* Compares the computed mark passings to the given ones. For each given sequence of mark passings, the one with the
* timestamp closest to the given data is selected.
*
* @param markPassings
* - a {@link Map} from each {@link Competitor} to a {@link Map} from each {@link Waypoint} to a
* {@link List} of mark passing sequences
* @return how many mark passings were missed or detected way too early or too late
*/
protected int compareCalculatedAndGivenPassings(List<MarkPassing> markPassings, boolean printDebug) {
int overallMisses = 0;
Iterable<Competitor> competitors = getRace().getCompetitors();
for (Competitor c : competitors) {
int misses = 0;
NavigableSet<MarkPassing> givenPassingsForCompetitor = getTrackedRace().getMarkPassings(c);
List<MarkPassing> calculatedPassingsForCompetitor = new ArrayList<MarkPassing>();
for (MarkPassing p : markPassings) {
if (p.getCompetitor().equals(c)) {
calculatedPassingsForCompetitor.add(p);
}
}
if (printDebug)
System.out.println("Competitor is " + c.getName());
// Simultaneously iterate over given and computed mark passings and calculate the timedelta
ListIterator<MarkPassing> calculatedPassingsIt = calculatedPassingsForCompetitor.listIterator();
for (MarkPassing givenPassing : givenPassingsForCompetitor) {
Waypoint passedWaypoint = givenPassing.getWaypoint();
MarkPassing calculatedPassing;
if (calculatedPassingsIt.hasNext()) {
calculatedPassing = calculatedPassingsIt.next();
} else {
calculatedPassing = null;
}
if (calculatedPassing != null && calculatedPassing.getWaypoint().equals(passedWaypoint)) {
long timedelta = calculatedPassing.getTimePoint().asMillis()
- givenPassing.getTimePoint().asMillis();
String waypointType = isGate(passedWaypoint) ? "Gate" : "Buoy";
if (Math.abs(timedelta) < TIMEDELTA_TOLERANCE) {
// counts as detected
if (printDebug)
System.out.println("\tTimedelta of calculated mark passing for waypoint "
+ passedWaypoint.getName() + " (" + waypointType + "): " + timedelta + "ms, "
+ calculatedPassing.getTimePoint().asDate() + "(computed) vs. "
+ givenPassing.getTimePoint().asDate() + "(given)");
} else {
// timedelta too huge, counts as missed
misses++;
overallMisses++;
if (printDebug)
System.out.println("\tPassings of waypoint " + passedWaypoint.getName() + " ("
+ waypointType + ") are detected way off (" + timedelta + "ms, "
+ calculatedPassing.getTimePoint().asDate() + ")");
}
} else {
// mark passing for given waypoint
misses++;
overallMisses++;
if (printDebug)
System.out.println("\tNo passings of waypoint " + passedWaypoint.getName());
if (calculatedPassingsIt.hasPrevious()) {
// rewind the iterator to the previous entry
calculatedPassingsIt.previous();
}
}
}
if (printDebug)
System.out.println("\t" + misses + " waypoints were missed.");
}
if (printDebug)
System.out.println("Overall, " + overallMisses + " mark passings were missed.");
return overallMisses;
}
/**
* Get the previous {@link Waypoint} of a given {@link Waypoint}.
*
* @param waypoint
* - the {@link Waypoint} to obtain the predecessor of
* @return a {@link Waypoint} that has to be passed before the given {@link Waypoint} of the {@link Course}, or null
* if none
*/
protected Waypoint getPreviousWaypoint(Waypoint waypoint) {
Course course = getRace().getCourse();
try {
course.lockForRead();
int i = course.getIndexOfWaypoint(waypoint);
for (Waypoint w : course.getWaypoints()) {
if (course.getIndexOfWaypoint(w) == i - 1) {
return w;
}
}
} finally {
course.unlockAfterRead();
}
return null;
}
/**
* Get the next {@link Waypoint} after the given {@link Waypoint}.
*
* @param waypoint
* - the {@link Waypoint} to obtain the successor of
* @return a {@link Waypoint} that has to be passed after the given {@link Waypoint} of the {@link Course}, or null
* if none
*/
protected Waypoint getNextWaypoint(Waypoint waypoint) {
Course course = getRace().getCourse();
try {
course.lockForRead();
int i = course.getIndexOfWaypoint(waypoint);
for (Waypoint w : course.getWaypoints()) {
if (course.getIndexOfWaypoint(w) == i + 1) {
return w;
}
}
} finally {
course.unlockAfterRead();
}
return null;
}
/**
* Get the passing side of any given {@link Mark}. For single buoys this returns
* {@link Waypoint#getPassingInstructions()}, for gates, the passing side of the {@link Waypoint}'s first
* {@link Mark} is calculated and depending on the given {@link Mark} is returned unchanged (m = Mark 1) or inverted
* (m = Mark 2). Since the calculation of the passing side uses the {@link DynamicGPSFixTrack} of the
* {@link Waypoint}'s {@link Mark}s, a {@link TimePoint} has to be specified.
*
* @param waypoint
* - the {@link Waypoint} to get the passing side of
* @param m
* - the {@link Mark} of the {@link Waypoint} to get the passing side of
* @param timePoint
* - the {@link TimePoint} to get the passing side at
* @return the {@link NauticSide} the given {@link Mark} of the given {@link WayPoint} has to be passed on at the
* given {@link TimePoint}
*/
protected PassingInstruction getPassingInstructionOfMark(Waypoint waypoint, Mark m, TimePoint timePoint) {
if (isGate(waypoint)) {
PassingInstruction mark1PassingInstruction = getPassingInstructionForMark1OfGate(waypoint, timePoint);
if (m.equals(waypoint.getMarks().iterator().next())) {
// m is mark 1 of gate
return mark1PassingInstruction;
} else if (mark1PassingInstruction.equals(PassingInstruction.Starboard)) {
// m is mark 2 of gate -> opposite PassingInstruction of mark 1
return PassingInstruction.Port;
} else {
return PassingInstruction.Starboard;
}
} else {
// waypoint is not a gate
return waypoint.getPassingInstructions();
}
}
/**
* Calculate the passing side of {@link Mark} 1 of the given {@link Waypoint} at the given {@link TimePoint}.
*
* @param waypoint
* - the {@link Waypoint} to calculate the passing side for
* @param timePoint
* - the {@link TimePoint} to calculate the passing side at
* @return the {@link NauticSide} the {@link Mark} 1 of the given {@link Waypoint} has to be passed on
*/
protected PassingInstruction getPassingInstructionForMark1OfGate(Waypoint waypoint, TimePoint timePoint) {
if (!isGate(waypoint)) {
return null;
}
// calculate the passing side of the first mark in the waypoints marks
Position mark1Pos = null;
Iterator<Mark> markIt = waypoint.getMarks().iterator();
DynamicGPSFixTrack<Mark, GPSFix> mark1Track = getTrackedRace().getOrCreateTrack(markIt.next());
try {
mark1Track.lockForRead();
mark1Pos = mark1Track.getFirstRawFixAtOrAfter(timePoint).getPosition();
} finally {
mark1Track.unlockAfterRead();
}
Position mark2Pos = null;
DynamicGPSFixTrack<Mark, GPSFix> mark2Track = getTrackedRace().getOrCreateTrack(markIt.next());
try {
mark2Track.lockForRead();
mark2Pos = mark2Track.getFirstRawFixAtOrAfter(timePoint).getPosition();
} finally {
mark2Track.unlockAfterRead();
}
if (getRace().getCourse().getFirstWaypoint().equals(waypoint)) {
// first waypoint of the course, racing direction is towards next waypoint
Position nextWpPos = getTrackedRace().getApproximatePosition(getNextWaypoint(waypoint), timePoint);
Bearing mark1ToNextWp = mark1Pos.getBearingGreatCircle(nextWpPos);
Bearing mark2ToNextWp = mark2Pos.getBearingGreatCircle(nextWpPos);
if (mark1ToNextWp.getDifferenceTo(mark2ToNextWp).getDegrees() > 0) {
return PassingInstruction.Starboard;
} else {
return PassingInstruction.Port;
}
} else {
// racing direction is from previous waypoint
Position prevWpPos = getTrackedRace().getApproximatePosition(getPreviousWaypoint(waypoint), timePoint);
Bearing prevWpToMark1 = prevWpPos.getBearingGreatCircle(mark1Pos);
Bearing prevWpToMark2 = prevWpPos.getBearingGreatCircle(mark2Pos);
if (prevWpToMark1.getDifferenceTo(prevWpToMark2).getDegrees() > 0) {
return PassingInstruction.Port;
} else {
return PassingInstruction.Starboard;
}
}
}
/**
* Checks if a {@link Waypoint} has exactly 2 {@link Mark Marks}.
*
* @param wp
* the {@link Waypoint} to check
* @return <code>true</code> if {@link Waypoint#getMarks()} returns an {@link Iterable} with exactly 2 elements.
*/
protected boolean isGate(Waypoint wp) {
Iterator<Mark> it = wp.getMarks().iterator();
if (it != null && it.hasNext()) {
it.next();
if (it.hasNext()) {
it.next();
if (!it.hasNext()) {
return true;
}
}
}
return false;
}
/**
* Calculates the passing {@link Bearing} of the given {@link Mark} which belongs to the given {@link Waypoint} at
* the given {@link TimePoint}.
*
* @param waypoint
* - the {@link Waypoint} the given {@link Mark} belongs to
* @param mark
* - the {@link Mark} to calculate the passing {@link Bearing} for
* @param time
* - the {@link TimePoint} at which to calculate the passing {@link Bearing}
* @return a {@link Bearing} that represents the line a boat has to pass in order to round the given {@link Mark}
*/
protected Bearing getPassingBearing(Waypoint waypoint, Mark mark, TimePoint time) {
Course course = getRace().getCourse();
Bearing passingBearing = null;
Position markPos = null;
DynamicGPSFixTrack<Mark, GPSFix> markTrack = getTrackedRace().getOrCreateTrack(mark);
try {
markTrack.lockForRead();
markPos = markTrack.getFirstRawFixAtOrAfter(time).getPosition();
} finally {
markTrack.unlockAfterRead();
}
if (waypoint.equals(course.getFirstWaypoint()) || waypoint.equals(course.getLastWaypoint())) {
// passing a line
Mark otherMark = null;
for (Mark m : waypoint.getMarks()) {
if (!m.equals(mark)) {
otherMark = m;
}
}
Position otherMarkPos = null;
DynamicGPSFixTrack<Mark, GPSFix> otherMarkTrack = getTrackedRace().getOrCreateTrack(otherMark);
try {
otherMarkTrack.lockForRead();
otherMarkPos = otherMarkTrack.getFirstRawFixAtOrAfter(time).getPosition();
} finally {
otherMarkTrack.unlockAfterRead();
}
passingBearing = markPos.getBearingGreatCircle(otherMarkPos);
} else {
// passing a single buoy
Bearing bearingToNextWp = null;
Bearing bearingfromPrevWp = null;
Bearing bearingDiff = null;
// calculate bearings to next and from previous waypoint
if (!course.getLastWaypoint().equals(waypoint)) {
// not last waypoint of the race
Waypoint nextWp = getNextWaypoint(waypoint);
Position nextWaypointPos = getTrackedRace().getApproximatePosition(nextWp, time);
bearingToNextWp = markPos.getBearingGreatCircle(nextWaypointPos);
}
if (!course.getFirstWaypoint().equals(waypoint)) {
// not first waypoint of the course
Waypoint prevWp = getPreviousWaypoint(waypoint);
Position prevWaypointPos = getTrackedRace().getApproximatePosition(prevWp, time);
bearingfromPrevWp = prevWaypointPos.getBearingGreatCircle(markPos);
}
// depending on passing side, set the bearing difference
PassingInstruction passingInstruction = getPassingInstructionOfMark(waypoint, mark, time);
if (passingInstruction != null && passingInstruction.equals(PassingInstruction.Starboard)) {
bearingDiff = new DegreeBearingImpl(-90);
} else {
bearingDiff = new DegreeBearingImpl(90);
}
// calculate the passing bearing
if (course.getFirstWaypoint().equals(waypoint)) {
passingBearing = bearingToNextWp.add(bearingDiff);
} else if (course.getLastWaypoint().equals(waypoint)) {
passingBearing = bearingfromPrevWp.add(bearingDiff);
} else {
passingBearing = bearingToNextWp.add(bearingDiff).middle(bearingfromPrevWp.add(bearingDiff));
}
}
return passingBearing;
}
/**
* Finds the {@link Entry} with the best {@link MarkPassing} of the key set. A {@link MarkPassing} is better than
* another if:
* <ol>
* <li>it is on the correct side of the mark and the other one is not</li>
* <li>it is closer to the mark than the other one</li>
* <li>its TimePoint is smaller (it happened earlier) than the other one</li>
* </ol>
*
* @param possiblePassings
* @return
*/
protected Entry<MarkPassing, Position> findBestMarkPassing(Map<MarkPassing, Position> passings) {
if (passings.size() == 1) {
return passings.entrySet().iterator().next();
} else if (passings.isEmpty()) {
return null;
}
Map<MarkPassing, Position> possibleResults = new HashMap<MarkPassing, Position>();
// check if mark was passed on correct side
for (Entry<MarkPassing, Position> entry : passings.entrySet()) {
// find out on which side of the mark was passed
MarkPassing passing = entry.getKey();
Position passPos = entry.getValue();
Position markPos = null;
DynamicGPSFixTrack<Mark, GPSFix> markTrack = getTrackedRace().getOrCreateTrack(
passing.getWaypoint().getMarks().iterator().next());
try {
markTrack.lockForRead();
markPos = markTrack.getLastFixAtOrBefore(passing.getTimePoint()).getPosition();
} finally {
markTrack.unlockAfterRead();
}
Bearing bearingDiff = markPos.getBearingGreatCircle(passPos).getDifferenceTo(
getPassingBearing(passing.getWaypoint(), passing.getWaypoint().getMarks().iterator().next(),
passing.getTimePoint()));
if (Math.abs(bearingDiff.getDegrees()) < 90) {
// passed on correct side of mark
possibleResults.put(entry.getKey(), entry.getValue());
}
}
if (possibleResults.size() > 0) {
// All possible passings are on correct side, pass them to the distance test.
// If all passings were on the wrong side, passings is not overwritten and they are still passed to the
// distance test.
passings.putAll(possibleResults);
}
possibleResults.clear();
// find the mark passing with the smallest distance to the mark
for (Entry<MarkPassing, Position> entry : passings.entrySet()) {
// find out how far away the mark was passed
MarkPassing passing = entry.getKey();
Position passPos = entry.getValue();
Position markPos = null;
DynamicGPSFixTrack<Mark, GPSFix> markTrack = getTrackedRace().getOrCreateTrack(
passing.getWaypoint().getMarks().iterator().next());
try {
markTrack.lockForRead();
markPos = markTrack.getLastFixAtOrBefore(passing.getTimePoint()).getPosition();
} finally {
markTrack.unlockAfterRead();
}
if (possibleResults.isEmpty()) {
possibleResults.put(passing, passPos);
} else {
Distance shortestDistance = markPos
.getDistance(possibleResults.entrySet().iterator().next().getValue());
Distance newDistance = markPos.getDistance(passPos);
if (shortestDistance.compareTo(newDistance) == 0) {
// same distance, add to possible results
possibleResults.put(passing, passPos);
} else if (shortestDistance.compareTo(newDistance) > 0) {
// shorter distance, remove all other possible results and add this one
possibleResults.clear();
possibleResults.put(passing, passPos);
}
}
}
if (possibleResults.size() == 1) {
// only one possible result left, we're done
return possibleResults.entrySet().iterator().next();
} else if (possibleResults.size() > 1) {
// all possible passings have the same distance to the mark, pass them to the timing test
passings.putAll(possibleResults);
}
possibleResults.clear();
// find the mark passing with the smallest timestamp
for (Entry<MarkPassing, Position> entry : passings.entrySet()) {
// find out how far away the mark was passed
MarkPassing passing = entry.getKey();
Position passPos = entry.getValue();
if (possibleResults.isEmpty()) {
possibleResults.put(passing, passPos);
} else {
TimePoint smallestTime = possibleResults.entrySet().iterator().next().getKey().getTimePoint();
TimePoint newTime = passing.getTimePoint();
if (smallestTime.compareTo(newTime) == 0) {
// same timestamp, add to possible results
possibleResults.put(passing, passPos);
} else if (smallestTime.compareTo(newTime) > 0) {
// smaller timestamp, remove all other possible results and add this one
possibleResults.clear();
possibleResults.put(passing, passPos);
}
}
}
if (possibleResults.size() > 0) {
// all possible passings have the same timestamp, just return the first one
return possibleResults.entrySet().iterator().next();
}
return null;
}
}
@@ -1,249 +0,0 @@
package com.sap.sailing.domain.test.markpassing;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.MarkPassing;
/**
* This class tests a mark passing detection algorithm that is based purely on bearing analyses
* from the {@link Course}'s {@link Mark}s to the GPS fixes of the {@link Competitor}'s boats.
* It uses a "hot zone" around the {@link Mark}s to avoid unnecessary testing of {@link Mark}s
* too far away from the boat.
*
* @author Martin Hanysz
*
*/
public class MartinMarkPassingBearingBasedTest extends MartinAbstractMarkPassingTest {
/**
* Controls the size of the hot zone around buoys. It is multiplied with the boat length.
*/
private static final double HOT_ZONE_TO_BOAT_LENGHT_RATIO = 10.0;
private Map<Competitor, Entry<MarkPassing, Position>> lastCompetitorPassings;
public MartinMarkPassingBearingBasedTest() throws MalformedURLException, URISyntaxException {
super();
lastCompetitorPassings = new HashMap<Competitor, Entry<MarkPassing, Position>>();
}
@Override
MarkPassing computeMarkPassings(Competitor competitor, GPSFixMoving fix) {
Map<MarkPassing, Position> possibleMarkPassings = new HashMap<MarkPassing, Position>();
// find out which waypoints to check (last waypoint passed and the next)
List<Waypoint> waypointsToCheck = new ArrayList<Waypoint>();
Course course = getRace().getCourse();
try {
course.lockForRead();
if (!lastCompetitorPassings.containsKey(competitor)) {
// no previous passings, competitor needs to pass 1st waypoint
waypointsToCheck.add(course.getFirstWaypoint());
} else {
// previous passing, competitor either passes same waypoint again or the next waypoint
waypointsToCheck.add(lastCompetitorPassings.get(competitor).getKey().getWaypoint());
Waypoint next = getNextWaypoint(lastCompetitorPassings.get(competitor).getKey().getWaypoint());
if (next != null) {
waypointsToCheck.add(next);
}
}
} finally {
course.unlockAfterRead();
}
for (Waypoint wp : waypointsToCheck) {
MarkPassing passing = null;
if (isGate(wp)) {
if (wp.equals(course.getFirstWaypoint()) ||
wp.equals(course.getLastWaypoint())) {
// start or finish line
passing = getLinePassing(wp, fix, competitor);
} else {
// waypoint is a gate, either pass mark 1 or mark 2 of the gate
Iterator<Mark> markIterator = wp.getMarks().iterator();
while (passing == null && markIterator.hasNext()) {
passing = getBuoyPassing(wp, markIterator.next(), fix, competitor);
}
}
} else {
// waypoint is single buoy
passing = getBuoyPassing(wp, wp.getMarks().iterator().next(), fix, competitor);
}
if (passing != null) {
// detected a passing
possibleMarkPassings.put(passing, fix.getPosition());
}
}
Entry<MarkPassing, Position> result = null;
if (possibleMarkPassings.size() > 0) {
Entry<MarkPassing, Position> lastPassing = lastCompetitorPassings.get(competitor);
Entry<MarkPassing, Position> possiblePassing = possibleMarkPassings.entrySet().iterator().next();
if (lastPassing != null &&
lastPassing.getKey().getWaypoint().equals(possiblePassing.getKey().getWaypoint())) {
// previous passing detected & passed same waypoint again
possibleMarkPassings.put(lastPassing.getKey(), lastPassing.getValue());
}
result = findBestMarkPassing(possibleMarkPassings);
}
if (result != null) {
lastCompetitorPassings.put(competitor, result);
return result.getKey();
} else {
return null;
}
}
/**
* Check if the given {@link Waypoint} was passed with the given {@link GPSFixMoving} of the given {@link Competitor}.
* @param waypoint - the {@link Waypoint} to check
* @param mark - the mark of the given {@link Waypoint} to be checked
* @param fix - the {@link GPSFixMoving} to check
* @param competitor - the {@link Competitor} the given fix belongs to
* @return a {@link MarkPassing} if the fix represents a passing of the given {@link Waypoint}, null if not
*/
private MarkPassing getBuoyPassing(Waypoint waypoint, Mark mark, GPSFixMoving fix, Competitor competitor) {
if (waypoint == null || getTrackedRace().getStartOfRace().compareTo(fix.getTimePoint()) > 0) {
// no waypoint or race not started yet
return null;
}
// get position of given mark
Position markPos = null;
DynamicGPSFixTrack<Mark, GPSFix> markTrack = getTrackedRace().getOrCreateTrack(mark);
try {
markTrack.lockForRead();
markPos = markTrack.getFirstRawFixAtOrAfter(fix.getTimePoint()).getPosition();
} finally {
markTrack.unlockAfterRead();
}
// check if boat is in "hot zone"
// boat in "hot zone" <=> distance from boat to mark < boat length * x
if (fix.getPosition().getDistance(markPos).compareTo(competitor.getBoat().getBoatClass().getHullLength().scale(HOT_ZONE_TO_BOAT_LENGHT_RATIO)) > 0) {
// not in "hot zone"
return null;
}
// calculate mark passing bearing for waypoint
Bearing markToBoatBearing = markPos.getBearingGreatCircle(fix.getPosition());
Bearing passingBearing = getPassingBearing(waypoint, mark, fix.getTimePoint());
PassingInstruction passingInstruction = getPassingInstructionOfMark(waypoint, mark, fix.getTimePoint());
double passingBearingDelta = markToBoatBearing.getDifferenceTo(passingBearing).getDegrees();
if (passingInstruction == null ||
(passingInstruction.equals(PassingInstruction.Port) &&
passingBearingDelta > 0)) {
// markToBoatBearing is smaller than passing bearing -> passed on port
return DomainFactory.INSTANCE.createMarkPassing(fix.getTimePoint(), waypoint, competitor);
} else if ( passingInstruction.equals(PassingInstruction.Starboard) &&
passingBearingDelta < 0) {
// markToBoatBearing is greater than passing bearing -> passed on stb
return DomainFactory.INSTANCE.createMarkPassing(fix.getTimePoint(), waypoint, competitor);
}
return null;
}
/**
* Check if the line represented by the given {@link Waypoint} was passed with the given {@link GPSFixMoving} of the given {@link Competitor}.
* @param waypoint - the {@link Waypoint} to check
* @param fix - the {@link GPSFixMoving} to check
* @param competitor - the {@link Competitor} the given fix belongs to
* @return a {@link MarkPassing} if the fix represents a passing of the line of the given {@link Waypoint}, null if not
*/
private MarkPassing getLinePassing(Waypoint waypoint, GPSFixMoving fix, Competitor competitor) {
if (waypoint == null || !isGate(waypoint)) {
// no waypoint, waypoint not a gate, or race not started yet
return null;
}
/* A boat passes the line, if the bearing from the boat to mark 1 of the gate equals
* the bearing from mark 2 of the gate to mark 1 of the gate AND the bearing from the boat
* to mark 2 of the gate equals the bearing from mark 1 of the gate to mark 2 of the gate.
*/
// get tracks of both marks
Iterator<Mark> markIt = waypoint.getMarks().iterator();
DynamicGPSFixTrack<Mark, GPSFix> mark1Track = null;
Mark mark1 = null;
DynamicGPSFixTrack<Mark, GPSFix> mark2Track = null;
Mark mark2 = null;
if (markIt.hasNext()) {
mark1 = markIt.next();
mark1Track = getTrackedRace().getOrCreateTrack(mark1);
if (markIt.hasNext())
mark2 = markIt.next();
mark2Track = getTrackedRace().getOrCreateTrack(mark2);
}
// get positions for both marks of the gate
Position mark1Pos = null;
Position mark2Pos = null;
try {
mark1Track.lockForRead();
try {
mark2Track.lockForRead();
mark1Pos = mark1Track.getFirstRawFixAtOrAfter(fix.getTimePoint()).getPosition();
mark2Pos = mark2Track.getFirstRawFixAtOrAfter(fix.getTimePoint()).getPosition();
} finally {
mark2Track.unlockAfterRead();
}
} finally {
mark1Track.unlockAfterRead();
}
// calculate if boat is in "hot zone" of gate
// in "hot zone" <=> distance from any mark of gate to boat < length of line between marks
Distance lineLength = mark1Pos.getDistance(mark2Pos);
if ( mark1Pos.getDistance(fix.getPosition()).compareTo(lineLength) > 0 &&
mark2Pos.getDistance(fix.getPosition()).compareTo(lineLength) > 0) {
// not in "hot zone"
return null;
}
// calculate bearings from boat to 1st and 2nd mark, from 1st mark to 2nd mark, and from 2nd mark to 1st mark
Bearing boatToMark1Bearing = fix.getPosition().getBearingGreatCircle(mark1Pos);
Bearing mark1ToMark2Bearing = mark1Pos.getBearingGreatCircle(mark2Pos);
Bearing boatToMark2Bearing = fix.getPosition().getBearingGreatCircle(mark2Pos);
Bearing mark2ToMark1Bearing = mark2Pos.getBearingGreatCircle(mark1Pos);
PassingInstruction PassingInstructionOfMark1OfGate = getPassingInstructionForMark1OfGate(waypoint, fix.getTimePoint());
double mark1PassingBearingDelta = boatToMark1Bearing.getDifferenceTo(mark2ToMark1Bearing).getDegrees();
double mark2PassingBearingDelta = boatToMark2Bearing.getDifferenceTo(mark1ToMark2Bearing).getDegrees();
//Mark closestMark = mark1;
if (mark2 != null && fix.getPosition().getDistance(mark1Pos).compareTo(fix.getPosition().getDistance(mark2Pos)) < 0) {
//closestMark = mark2;
}
if ( PassingInstructionOfMark1OfGate.equals(PassingInstruction.Starboard) &&
mark1PassingBearingDelta < 0 &&
mark2PassingBearingDelta > 0) {
// gate passed if mark1 had to be passed on stb
return DomainFactory.INSTANCE.createMarkPassing(fix.getTimePoint(), waypoint,competitor);
} else if ( PassingInstructionOfMark1OfGate.equals(PassingInstruction.Port) &&
mark1PassingBearingDelta > 0 &&
mark2PassingBearingDelta < 0) {
// gate passed if mark1 had to be passed on port
return DomainFactory.INSTANCE.createMarkPassing(fix.getTimePoint(), waypoint, competitor);
}
return null;
}
}
@@ -1,192 +0,0 @@
package com.sap.sailing.domain.test.markpassing;
import static org.junit.Assert.assertTrue;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.junit.Before;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.markpassingcalculation.splining.HermiteCurve;
import com.sap.sailing.domain.markpassingcalculation.splining.StraightLine;
import com.sap.sailing.domain.markpassingcalculation.splining.Vector2D;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sse.common.TimePoint;
/**
* This class tests a mark passing detection algorithm that interpolates the boats' positions between
* their GPS fixes using Hermite Splines.
* @author Martin Hanysz
*
*/
public class MartinMarkPassingSplineBasedTest extends MartinAbstractMarkPassingTest {
// a factor to scale the tangents calculated from boat speed and bearing
private static double TANGENT_SCALING_FACTOR = 100;
private Map<Competitor, Entry<MarkPassing, Position>> lastCompetitorPassings;
public MartinMarkPassingSplineBasedTest() throws MalformedURLException, URISyntaxException {
super();
lastCompetitorPassings = new HashMap<Competitor, Entry<MarkPassing, Position>>();
}
@Before
public void setTangentScaling() {
TANGENT_SCALING_FACTOR = 100.0;
}
public void testTangentScaling() {
Map<Double, Integer> results = new HashMap<Double, Integer>();
double s = 100.0;
double div = 10.0;
while (s > 0.000000001) {
MartinMarkPassingSplineBasedTest.TANGENT_SCALING_FACTOR = s;
List<MarkPassing> markPassings = computeAllMarkPassings();
// compare computed mark passings to given ones
int overallMisses = compareCalculatedAndGivenPassings(markPassings, false);
results.put(s, overallMisses);
s /= div;
}
boolean success = false;
for (Entry<Double, Integer> e : results.entrySet()) {
System.out.println("Scaling: " + e.getKey() + ", Misses: " + e.getValue());
if (e.getValue() == 0) {
success = true;
}
}
assertTrue("No setting for the scaling factor resulted in 0 missed waypoints.", success);
}
/* (non-Javadoc)
* @see com.sap.sailing.domain.test.AbstractMarkPassingTest#computeMarkPassings()
*/
@Override
MarkPassing computeMarkPassings(Competitor competitor, GPSFixMoving fix) {
// interpolate the course of the competitor from the previous fix to the given fix
DynamicGPSFixTrack<Competitor, GPSFixMoving> competitorTrack = getTrackedRace().getTrack(competitor);
GPSFixMoving prevFix = null;
try {
competitorTrack.lockForRead();
prevFix = competitorTrack.getLastRawFixBefore(fix.getTimePoint());
} finally {
competitorTrack.unlockAfterRead();
}
if (prevFix == null) {
return null;
}
Vector2D point1 = new Vector2D(prevFix.getPosition());
Vector2D point2 = new Vector2D(fix.getPosition());
Vector2D tangent1 = new Vector2D(prevFix.getSpeed().getBearing(), prevFix.getSpeed().getKnots() * TANGENT_SCALING_FACTOR /* abh�ngig von Bootsklasse, zeitl. Abstand zw. Fixes */);
Vector2D tangent2 = new Vector2D(fix.getSpeed().getBearing(), fix.getSpeed().getKnots() * TANGENT_SCALING_FACTOR);
HermiteCurve interpolatedCourse = new HermiteCurve(point1, point2, tangent1, tangent2);
// find out which waypoints to check (last waypoint passed and the next)
List<Waypoint> waypointsToCheck = new ArrayList<Waypoint>();
Course raceCourse = getRace().getCourse();
try {
raceCourse.lockForRead();
if (!lastCompetitorPassings.containsKey(competitor)) {
// no previous passings, competitor needs to pass 1st waypoint
waypointsToCheck.add(raceCourse.getFirstWaypoint());
} else {
waypointsToCheck.add(lastCompetitorPassings.get(competitor).getKey().getWaypoint());
Waypoint next = getNextWaypoint(lastCompetitorPassings.get(competitor).getKey().getWaypoint());
if (next != null) {
waypointsToCheck.add(next);
}
}
} finally {
raceCourse.unlockAfterRead();
}
// check if the interpolated course passes one of the waypoints to check
Map<MarkPassing, Position> possiblePassings = new HashMap<MarkPassing, Position>();
for (Waypoint wp : waypointsToCheck) {
StraightLine passingLine = null;
List<Position> markPositions = new ArrayList<Position>();
for (Mark m : wp.getMarks()) {
DynamicGPSFixTrack<Mark, GPSFix> markTrack = getTrackedRace().getOrCreateTrack(m);
try {
markTrack.lockForRead();
markPositions.add(markTrack.getLastFixAtOrBefore(fix.getTimePoint()).getPosition());
} finally {
markTrack.unlockAfterRead();
}
}
Vector2D mark1Pos = new Vector2D(markPositions.get(0));
Map<Vector2D, Double> intersections = null;
//Mark passedMark = wp.getMarks().iterator().next();
if (isGate(wp)) {
if (wp.equals(raceCourse.getFirstWaypoint()) || wp.equals(raceCourse.getLastWaypoint())) {
// passing start or finish line
Vector2D mark2Pos = new Vector2D(markPositions.get(1));
passingLine = new StraightLine(mark1Pos, mark2Pos.subtract(mark1Pos));
intersections = interpolatedCourse.intersectWith(passingLine);
} else {
// passing either mark of the gate
for (Mark mark : wp.getMarks()) {
Bearing passingBearing = getPassingBearing(wp, mark, fix.getTimePoint());
Vector2D passingVector = new Vector2D(passingBearing, 1.0);
passingLine = new StraightLine(mark1Pos, passingVector);
intersections = interpolatedCourse.intersectWith(passingLine);
if (intersections.size() > 0) {
// already found a passing of a mark
//passedMark = mark;
break;
}
}
}
} else {
// passing single buoy
Bearing passingBearing = getPassingBearing(wp, wp.getMarks().iterator().next(), fix.getTimePoint());
Vector2D passingVector = new Vector2D(passingBearing, 1.0);
passingLine = new StraightLine(mark1Pos, passingVector);
intersections = interpolatedCourse.intersectWith(passingLine);
}
for (Entry<Vector2D, Double> entry : intersections.entrySet()) {
TimePoint time = fix.getTimePoint();
TimePoint prevTime = prevFix.getTimePoint();
// t_pass = t_fix - (t_prev - t_fix) * interpolationParameterOfIntersection
// If the intersection is at 75% of the interpolated course, t_pass is at 75% between t_prev and t_fix.
// This cast truncates the milliseconds, which is acceptable at the moment.
TimePoint timePoint = time.minus((long) ((prevTime.asMillis() - time.asMillis()) * entry.getValue()));
possiblePassings.put(DomainFactory.INSTANCE.createMarkPassing(timePoint, wp, competitor), new DegreePosition(entry.getKey().y(), entry.getKey().x()));
}
}
Entry<MarkPassing, Position> result = null;
if (possiblePassings.size() > 0) {
Entry<MarkPassing, Position> lastPassing = lastCompetitorPassings.get(competitor);
Entry<MarkPassing, Position> possiblePassing = possiblePassings.entrySet().iterator().next();
if (lastPassing != null &&
lastPassing.getKey().getWaypoint().equals(possiblePassing.getKey().getWaypoint())) {
// previous passing detected & passed same waypoint again
possiblePassings.put(lastPassing.getKey(), lastPassing.getValue());
}
result = findBestMarkPassing(possiblePassings);
}
if (result != null) {
lastCompetitorPassings.put(competitor, result);
return result.getKey();
} else {
return null;
}
}
}
@@ -107,16 +107,16 @@ public class RaceRecord {
throw e;
}
if (loadLiveAndStoredURI) {
Map<String, String> paramURLContents = parseParams(paramURL);
String liveURIAsString = paramURLContents.get(LIVE_URI_PROPERTY);
liveURI = liveURIAsString == null ? null : new URI(liveURIAsString);
String storedURIAsString = paramURLContents.get(STORED_URI_PROPERTY);
if (storedURIAsString == null || storedURIAsString.startsWith("tcp:") || storedURIAsString.startsWith("http:") ||
storedURIAsString.startsWith("https:")) {
storedURI = storedURIAsString == null ? null : new URI(storedURIAsString);
} else {
storedURI = new URI(baseURL+"/"+storedURIAsString);
}
Map<String, String> paramURLContents = parseParams(paramURL);
String liveURIAsString = paramURLContents.get(LIVE_URI_PROPERTY);
liveURI = liveURIAsString == null ? null : new URI(liveURIAsString);
String storedURIAsString = paramURLContents.get(STORED_URI_PROPERTY);
if (storedURIAsString == null || storedURIAsString.startsWith("tcp:") || storedURIAsString.startsWith("http:") ||
storedURIAsString.startsWith("https:")) {
storedURI = storedURIAsString == null ? null : new URI(storedURIAsString);
} else {
storedURI = new URI(baseURL+"/"+storedURIAsString);
}
} else {
paramURLAsString = null;
liveURI = null;
@@ -11,13 +11,13 @@ public interface TracTracConnectionConstants {
final int PORT_STORED = PORT_LIVE + 1; //For official usage use 4401;
/*
* ‘Status’ is the one that we used originally and is an enumerate with the values:
* "Status" is the one that we used originally and is an enumerate with the values:
* - HIDDEN: the race is hidden
* - OFFLINE: the race is visible but it can not be loaded
* - ONLINE: the race is visible and online
* - REPLAY: the race is online and the mtb-file has been created (this value is no longer being used as we no longer display information about whether data are being retrieved from data base or file)
* ‘Visibility’ was introduced this year to improve some short comings of the ‘status’ attribute and is an enumerate with the values:
* "Visibility" was introduced this year to improve some short comings of the "status" attribute and is an enumerate with the values:
* - HIDDEN: the race is hidden (same meaning as above)
* - OFFLINE: the race is visible but it can not be loaded (same meaning as above)
* - ONLINE: the race can be loaded and it is in a live status (live or future)
@@ -1,78 +1,78 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.google.gdt.eclipse.suite.webapp">
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
<stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9876"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart"/>
<listEntry value="com.sap.sailing.gwt.ui.RaceBoard"/>
<listEntry value="com.sap.sailing.gwt.ui.DataMining"/>
<listEntry value="com.sap.sailing.gwt.ui.LeaderboardEditing"/>
<listEntry value="com.sap.sailing.gwt.ui.Spectator"/>
<listEntry value="com.sap.sailing.gwt.regattaoverview.RegattaOverview"/>
<listEntry value="com.sap.sailing.gwt.ui.Leaderboard"/>
<listEntry value="com.sap.sailing.gwt.ui.Simulator"/>
<listEntry value="com.sap.sailing.gwt.ui.YoutubePopup"/>
<listEntry value="com.sap.sailing.gwt.AutoPlay"/>
<listEntry value="com.sap.sailing.gwt.ui.AdminConsole"/>
<listEntry value="com.sap.sailing.gwt.home.Home"/>
<listEntry value="com.sap.sailing.gwt.AutoPlay"/>
</listAttribute>
<booleanAttribute key="com.google.gwt.eclipse.core.SUPERDEVMODE_ENABLED" value="true"/>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/Home.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.gwt.ui"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="false"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.googlecode.java-diff-utils/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.json.simple/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.server/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.expeditionconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.udpconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.mongodb/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.operationaltransformation/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.replication/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.moxieapps.gwt.highcharts/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.geocoding/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.google.gwt.osgi/lib/gwt-user.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;com.sap.sailing.gwt.ui&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt.adminconsole/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.polars.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.datamining.annotations/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-superDevMode -incremental -workDir &quot;${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work&quot; -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9876 -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/EmbeddedMapAndWindChart com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /gwt/AutoPlay.html com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms1024m -Xmx3072m"/>
</launchConfiguration>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.google.gdt.eclipse.suite.webapp">
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
<stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9876"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart"/>
<listEntry value="com.sap.sailing.gwt.ui.RaceBoard"/>
<listEntry value="com.sap.sailing.gwt.ui.DataMining"/>
<listEntry value="com.sap.sailing.gwt.ui.LeaderboardEditing"/>
<listEntry value="com.sap.sailing.gwt.ui.Spectator"/>
<listEntry value="com.sap.sailing.gwt.regattaoverview.RegattaOverview"/>
<listEntry value="com.sap.sailing.gwt.ui.Leaderboard"/>
<listEntry value="com.sap.sailing.gwt.ui.Simulator"/>
<listEntry value="com.sap.sailing.gwt.ui.YoutubePopup"/>
<listEntry value="com.sap.sailing.gwt.AutoPlay"/>
<listEntry value="com.sap.sailing.gwt.ui.AdminConsole"/>
<listEntry value="com.sap.sailing.gwt.home.Home"/>
<listEntry value="com.sap.sailing.gwt.AutoPlay"/>
</listAttribute>
<booleanAttribute key="com.google.gwt.eclipse.core.SUPERDEVMODE_ENABLED" value="true"/>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/Home.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.gwt.ui"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="false"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.googlecode.java-diff-utils/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.json.simple/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.server/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.expeditionconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.udpconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.mongodb/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.operationaltransformation/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.replication/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.moxieapps.gwt.highcharts/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.geocoding/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.google.gwt.osgi/lib/gwt-user.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;com.sap.sailing.gwt.ui&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt.adminconsole/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.polars.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.datamining.annotations/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-superDevMode -incremental -workDir &quot;${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work&quot; -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9876 -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/EmbeddedMapAndWindChart com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /gwt/AutoPlay.html com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms1024m -Xmx3072m"/>
</launchConfiguration>
+75 -75
View File
@@ -1,75 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.google.gdt.eclipse.suite.webapp">
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
<booleanAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT_AUTO" value="true"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.gwt.ui.RaceBoard"/>
<listEntry value="com.sap.sailing.gwt.ui.DataMining"/>
<listEntry value="com.sap.sailing.gwt.ui.LeaderboardEditing"/>
<listEntry value="com.sap.sailing.gwt.ui.Spectator"/>
<listEntry value="com.sap.sailing.gwt.regattaoverview.RegattaOverview"/>
<listEntry value="com.sap.sailing.gwt.ui.Leaderboard"/>
<listEntry value="com.sap.sailing.gwt.ui.Simulator"/>
<listEntry value="com.sap.sailing.gwt.ui.YoutubePopup"/>
<listEntry value="com.sap.sailing.gwt.AutoPlay"/>
<listEntry value="com.sap.sailing.gwt.ui.AdminConsole"/>
<listEntry value="com.sap.sailing.gwt.home.Home"/>
<listEntry value="com.sap.sailing.gwt.AutoPlay"/>
</listAttribute>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/Home.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.gwt.ui"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="false"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.googlecode.java-diff-utils/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.json.simple/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.server/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.expeditionconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.udpconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.mongodb/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.operationaltransformation/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.replication/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.moxieapps.gwt.highcharts/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.geocoding/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.google.gwt.osgi/lib/gwt-user.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;com.sap.sailing.gwt.ui&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt.adminconsole/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.polars.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -incremental -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /gwt/AutoPlay.html com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false"/>
</launchConfiguration>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.google.gdt.eclipse.suite.webapp">
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
<booleanAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT_AUTO" value="true"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.gwt.ui.RaceBoard"/>
<listEntry value="com.sap.sailing.gwt.ui.DataMining"/>
<listEntry value="com.sap.sailing.gwt.ui.LeaderboardEditing"/>
<listEntry value="com.sap.sailing.gwt.ui.Spectator"/>
<listEntry value="com.sap.sailing.gwt.regattaoverview.RegattaOverview"/>
<listEntry value="com.sap.sailing.gwt.ui.Leaderboard"/>
<listEntry value="com.sap.sailing.gwt.ui.Simulator"/>
<listEntry value="com.sap.sailing.gwt.ui.YoutubePopup"/>
<listEntry value="com.sap.sailing.gwt.AutoPlay"/>
<listEntry value="com.sap.sailing.gwt.ui.AdminConsole"/>
<listEntry value="com.sap.sailing.gwt.home.Home"/>
<listEntry value="com.sap.sailing.gwt.AutoPlay"/>
</listAttribute>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/Home.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.gwt.ui"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="false"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.googlecode.java-diff-utils/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.json.simple/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.server/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.expeditionconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.udpconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.mongodb/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.operationaltransformation/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.replication/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.moxieapps.gwt.highcharts/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.geocoding/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.google.gwt.osgi/lib/gwt-user.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;com.sap.sailing.gwt.ui&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt.adminconsole/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.polars.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -incremental -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /gwt/AutoPlay.html com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false"/>
</launchConfiguration>
File diff suppressed because it is too large. Load diff
@@ -1,86 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and modification follow.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
@@ -0,0 +1,17 @@
Jdom License
Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the disclaimer that follows these conditions in the documentation and/or other materials provided with the distribution.
3. The name "JDOM" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact {request_AT_jdom_DOT_org}.
4. Products derived from this software may not be called "JDOM", nor may "JDOM" appear in their name, without prior written permission from the JDOM Project Management {request_AT_jdom_DOT_org}.
In addition, we request (but do not require) that you include in the end-user documentation provided with the redistribution and/or in the software itself an acknowledgement equivalent to the following:
      "This product includes software developed by the JDOM Project (http://www.jdom.org/)."
Alternatively, the acknowledgment may be graphical using the logos available at http://www.jdom.org/images/logos.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -50,7 +50,7 @@ public class ImprintViewImpl extends Composite implements ImprintView {
if (c == null) {
return "--";
} else {
return c.getName();
return c.getName() + ", v." + c.getVersion();
}
}
@@ -59,7 +59,7 @@ public class ImprintViewImpl extends Composite implements ImprintView {
if (c == null) {
appendable.append("--");
} else {
appendable.append(c.getName());
appendable.append(c.getName() + ", v." + c.getVersion());
}
}
});
@@ -51,7 +51,7 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
protected final ListBox courseAreaListBox;
protected final ListBox sailingEventsListBox;
protected final CheckBox useStartTimeInferenceCheckBox;
private final ListEditorComposite<SeriesDTO> seriesEditor;
protected final ListEditorComposite<SeriesDTO> seriesEditor;
private final ListBox rankingMetricListBox;
protected final List<EventDTO> existingEvents;
@@ -0,0 +1,19 @@
package com.sap.sailing.gwt.ui.adminconsole;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
public class CreateDefaultLeaderboardGroupDialog extends DataEntryDialog<Void> {
public CreateDefaultLeaderboardGroupDialog(SailingServiceAsync sailingService, StringMessages stringMessages,
ErrorReporter errorReporter, DialogCallback<Void> dialogCallBack) {
super(stringMessages.createDefaultLeaderboardGroup(), /* message */ stringMessages.createDefaultLeaderboardGroup(),
stringMessages.yes(), stringMessages.no(), /* validator */ null, dialogCallBack);
}
@Override
protected Void getResult() {
return null;
}
}
@@ -8,7 +8,7 @@ import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
public class CreateDefaultRegattaLeaderboardDialog extends DataEntryDialog<RegattaIdentifier>{
public class CreateDefaultRegattaLeaderboardDialog extends DataEntryDialog<RegattaIdentifier> {
private RegattaDTO regatta;
@@ -39,5 +39,7 @@ public class EventCreateDialog extends EventDialog {
isPublicCheckBox.setValue(false);
imagesListComposite.fillImages(Collections.<ImageDTO>emptyList());
videosListComposite.fillVideos(Collections.<VideoDTO>emptyList());
// add default course area
courseAreaNameList.setValue(Collections.singletonList("Default"));
}
}
@@ -9,6 +9,8 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
@@ -230,7 +232,6 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
@Override
public void show() {
super.show();
nameEntryField.setFocus(true);
Scheduler.get().scheduleFinally(new ScheduledCommand() { @Override public void execute() { nameEntryField.setFocus(true); }});
}
}
@@ -32,6 +32,7 @@ import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.sap.sailing.gwt.ui.adminconsole.LeaderboardGroupDialog.LeaderboardGroupDescriptor;
import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory;
import com.sap.sailing.gwt.ui.client.EventsRefresher;
import com.sap.sailing.gwt.ui.client.LeaderboardGroupsDisplayer;
@@ -41,6 +42,7 @@ import com.sap.sailing.gwt.ui.client.shared.controls.FlushableCellTable;
import com.sap.sailing.gwt.ui.client.shared.controls.SelectionCheckboxColumn;
import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.EventBaseDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
@@ -48,6 +50,7 @@ import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.util.NaturalComparator;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.async.MarkedAsyncCallback;
import com.sap.sse.gwt.client.celltable.EntityIdentityComparator;
import com.sap.sse.gwt.client.celltable.RefreshableMultiSelectionModel;
import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
@@ -433,7 +436,7 @@ public class EventListComposite extends Composite implements EventsRefresher, Le
private void openCreateEventDialog() {
List<EventDTO> existingEvents = new ArrayList<EventDTO>(eventListDataProvider.getList());
List<LeaderboardGroupDTO> existingLeaderboardGroups = new ArrayList<LeaderboardGroupDTO>();
final List<LeaderboardGroupDTO> existingLeaderboardGroups = new ArrayList<LeaderboardGroupDTO>();
Util.addAll(availableLeaderboardGroups, existingLeaderboardGroups);
EventCreateDialog dialog = new EventCreateDialog(Collections.unmodifiableCollection(existingEvents), existingLeaderboardGroups,
sailingService, stringMessages, new DialogCallback<EventDTO>() {
@@ -442,12 +445,57 @@ public class EventListComposite extends Composite implements EventsRefresher, Le
}
@Override
public void ok(EventDTO newEvent) {
createNewEvent(newEvent);
public void ok(final EventDTO newEvent) {
createNewEvent(newEvent, existingLeaderboardGroups);
}
});
dialog.show();
}
/**
* @param newEvent the new event as created by the server, already including a valid {@link EventBaseDTO#id} value.
*/
private void openLeaderboardGroupCreationDialog(final List<LeaderboardGroupDTO> existingLeaderboardGroups, final EventDTO newEvent) {
LeaderboardGroupCreateDialog leaderboardGroupCreateDialog = new LeaderboardGroupCreateDialog(existingLeaderboardGroups, stringMessages, new DialogCallback<LeaderboardGroupDialog.LeaderboardGroupDescriptor>() {
@Override
public void ok(final LeaderboardGroupDescriptor newGroup) {
sailingService.createLeaderboardGroup(newGroup.getName(), newGroup.getDescription(),
newGroup.getDisplayName(), newGroup.isDisplayLeaderboardsInReverseOrder(),
newGroup.getOverallLeaderboardDiscardThresholds(), newGroup.getOverallLeaderboardScoringSchemeType(), new MarkedAsyncCallback<LeaderboardGroupDTO>(
new AsyncCallback<LeaderboardGroupDTO>() {
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to create new leaderboard group" + newGroup.getName()
+ ": " + t.getMessage());
}
@Override
public void onSuccess(LeaderboardGroupDTO newGroup) {
newEvent.addLeaderboardGroup(newGroup);
// fillEvents() will have replaced newEvent in allEvents by a new copy coming from the server which
// doesn't know about the new leaderboard group yet. An updateEvent call will link the leaderboard group
// to the event on the server
EventDTO matchingEvent = null;
for (EventDTO event : allEvents) {
if (event.id.equals(newEvent.id)) {
matchingEvent = event;
}
}
if (matchingEvent != null) {
updateEvent(matchingEvent, newEvent);
} else {
errorReporter.reportError("Could not find the event with name "+newEvent.getName()+" to which the leaderboardgroup should be added");
}
}
}));
}
@Override
public void cancel() {
}
});
leaderboardGroupCreateDialog.setFieldsBasedOnEventName(newEvent.getName());
leaderboardGroupCreateDialog.show();
}
private void openEditEventDialog(final EventDTO selectedEvent) {
List<EventDTO> existingEvents = new ArrayList<EventDTO>(eventListDataProvider.getList());
@@ -548,7 +596,7 @@ public class EventListComposite extends Composite implements EventsRefresher, Le
return new Pair<List<CourseAreaDTO>, List<CourseAreaDTO>>(courseAreasToAdd, courseAreasToRemove);
}
private void createNewEvent(final EventDTO newEvent) {
private void createNewEvent(final EventDTO newEvent, final List<LeaderboardGroupDTO> existingLeaderboardGroups) {
List<String> courseAreaNames = new ArrayList<String>();
for (CourseAreaDTO courseAreaDTO : newEvent.venue.getCourseAreas()) {
courseAreaNames.add(courseAreaDTO.getName());
@@ -562,8 +610,21 @@ public class EventListComposite extends Composite implements EventsRefresher, Le
}
@Override
public void onSuccess(EventDTO newEvent) {
public void onSuccess(final EventDTO newEvent) {
fillEvents();
if (newEvent.getLeaderboardGroups().isEmpty()) {
// show simple Dialog
new CreateDefaultLeaderboardGroupDialog(sailingService, stringMessages, errorReporter, new DialogCallback<Void>() {
@Override
public void ok(Void editedObject) {
openLeaderboardGroupCreationDialog(existingLeaderboardGroups, newEvent);
}
@Override
public void cancel() {
}
}).show();
}
}
});
}
@@ -23,4 +23,10 @@ public class LeaderboardGroupCreateDialog extends LeaderboardGroupDialog {
displayLeaderboardsInReverseOrderCheckBox.setValue(false);
useOverallLeaderboardCheckBox.setValue(false);
}
public void setFieldsBasedOnEventName(String name) {
nameEntryField.setText(name);
descriptionEntryField.setText(name);
validate();
}
}
@@ -0,0 +1,61 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
public class LinkRegattaLeaderboardToLeaderboardGroupOfEventDialog extends DataEntryDialog<LeaderboardGroupDTO> {
private final List<LeaderboardGroupDTO> leaderboardGroups;
private ListBox leaderboardGroupsBox;
public LinkRegattaLeaderboardToLeaderboardGroupOfEventDialog(SailingServiceAsync sailingService, final StringMessages stringMessages,
ErrorReporter errorReporter, StrippedLeaderboardDTO leaderboard, EventDTO event, DialogCallback<LeaderboardGroupDTO> callback) {
super(stringMessages.linkLeaderboardToLeaderboardGroupOfEvent(),
/*message*/ stringMessages.doYouWantToLinkLeaderboardToLeaderboardGroupOfEvent(leaderboard.name, event.getName()),
stringMessages.yes(), stringMessages.no(),
/*validator*/ new Validator<LeaderboardGroupDTO>() {
@Override
public String getErrorMessage(LeaderboardGroupDTO selectedLeaderboardGroup) {
final String errorMessage;
if (selectedLeaderboardGroup == null) {
errorMessage = stringMessages.pleaseSelectALeaderboardGroup();
} else {
errorMessage = null;
}
return errorMessage;
}
}, callback);
this.leaderboardGroups = new ArrayList<>();
leaderboardGroupsBox = createListBox(/* isMultipleSelect */ false);
for (final LeaderboardGroupDTO lgDTO : event.getLeaderboardGroups()) {
this.leaderboardGroups.add(lgDTO);
this.leaderboardGroupsBox.addItem(lgDTO.getName());
}
}
@Override
protected Widget getAdditionalWidget() {
return leaderboardGroupsBox;
}
@Override
protected LeaderboardGroupDTO getResult() {
final LeaderboardGroupDTO result;
if (leaderboardGroupsBox.getSelectedItemText() == null) {
result = null;
} else {
result = leaderboardGroups.get(leaderboardGroupsBox.getSelectedIndex());
}
return result;
}
}
@@ -6,6 +6,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.UUID;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
@@ -25,11 +26,14 @@ import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.RegattasDisplayer;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.async.MarkedAsyncCallback;
import com.sap.sse.gwt.client.celltable.RefreshableMultiSelectionModel;
import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
@@ -154,7 +158,6 @@ public class RegattaManagementPanel extends SimplePanel implements RegattasDispl
private void openCreateRegattaDialog() {
final Collection<RegattaDTO> existingRegattas = Collections.unmodifiableCollection(regattaListComposite.getAllRegattas());
sailingService.getEvents(new AsyncCallback<List<EventDTO>>() {
@Override
public void onFailure(Throwable caught) {
@@ -168,7 +171,7 @@ public class RegattaManagementPanel extends SimplePanel implements RegattasDispl
});
}
private void openCreateRegattaDialog(Collection<RegattaDTO> existingRegattas, List<EventDTO> existingEvents) {
private void openCreateRegattaDialog(Collection<RegattaDTO> existingRegattas, final List<EventDTO> existingEvents) {
RegattaWithSeriesAndFleetsCreateDialog dialog = new RegattaWithSeriesAndFleetsCreateDialog(existingRegattas, existingEvents, stringMessages,
new DialogCallback<RegattaDTO>() {
@Override
@@ -177,15 +180,14 @@ public class RegattaManagementPanel extends SimplePanel implements RegattasDispl
@Override
public void ok(RegattaDTO newRegatta) {
createNewRegatta(newRegatta);
openCreateDefaultRegattaLeaderboardDialog(newRegatta);
createNewRegatta(newRegatta, existingEvents);
}
});
dialog.ensureDebugId("RegattaCreateDialog");
dialog.show();
}
private void openCreateDefaultRegattaLeaderboardDialog(final RegattaDTO newRegatta){
private void openCreateDefaultRegattaLeaderboardDialog(final RegattaDTO newRegatta, final List<EventDTO> existingEvents) {
CreateDefaultRegattaLeaderboardDialog dialog = new CreateDefaultRegattaLeaderboardDialog(sailingService, stringMessages, errorReporter, newRegatta, new DialogCallback<RegattaIdentifier>() {
@Override
public void ok(RegattaIdentifier regattaIdentifier) {
@@ -199,7 +201,15 @@ public class RegattaManagementPanel extends SimplePanel implements RegattasDispl
@Override
public void onSuccess(StrippedLeaderboardDTO result) {
if (newRegatta.defaultCourseAreaUuid != null) {
// Show the event's leaderboard groups and allow the user to pick one to assign the regatta leaderboard to
final EventDTO event = getEventForCourseArea(existingEvents, newRegatta.defaultCourseAreaUuid);
if (!event.getLeaderboardGroups().isEmpty()) {
openRegattaLeaderboardToLeaderboardGroupOfEventLinkingDialog(result, event);
}
}
}
});
}
@@ -207,12 +217,66 @@ public class RegattaManagementPanel extends SimplePanel implements RegattasDispl
public void cancel() {
}
});
dialog.ensureDebugId("CreateDefaultRegattaDialog");
dialog.show();
}
private void createNewRegatta(final RegattaDTO newRegatta) {
/**
* When a new regatta with a new regatta leaderboard has been created, the user will now be given the chance to link
* the regatta leaderboard into a leaderboard group of the event out of which the regatta chose its default course area.
*
* @param newRegattaLeaderboard the new regatta leaderboard that the user may link now to a leaderboard group of an event
* @param eventToLinkRegattaTo an event that has at least one {@link EventDTO#getLeaderboardGroups() leaderboard group}
*/
private void openRegattaLeaderboardToLeaderboardGroupOfEventLinkingDialog(final StrippedLeaderboardDTO newRegattaLeaderboard, EventDTO eventToLinkRegattaTo) {
LinkRegattaLeaderboardToLeaderboardGroupOfEventDialog dialog = new LinkRegattaLeaderboardToLeaderboardGroupOfEventDialog(sailingService, stringMessages, errorReporter, newRegattaLeaderboard, eventToLinkRegattaTo,
new DialogCallback<LeaderboardGroupDTO>() {
@Override
public void ok(final LeaderboardGroupDTO selectedLeaderboardGroup) {
final List<String> leaderboardNames = new ArrayList<>();
for (StrippedLeaderboardDTO leaderboard : selectedLeaderboardGroup.getLeaderboards()) {
leaderboardNames.add(leaderboard.name);
}
leaderboardNames.add(newRegattaLeaderboard.name);
sailingService.updateLeaderboardGroup(selectedLeaderboardGroup.getName(), selectedLeaderboardGroup.getName(), selectedLeaderboardGroup.description,
selectedLeaderboardGroup.getDisplayName(), leaderboardNames, selectedLeaderboardGroup.getOverallLeaderboardDiscardThresholds(),
selectedLeaderboardGroup.getOverallLeaderboardScoringSchemeType(), new MarkedAsyncCallback<Void>(new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(stringMessages.failedToLinkLeaderboardToLeaderboardGroup(newRegattaLeaderboard.name, selectedLeaderboardGroup.getName()));
}
@Override
public void onSuccess(Void result) {
}
}));
}
@Override
public void cancel() {
}
});
dialog.ensureDebugId("LinkRegattaLeaderboardToLeaderboardGroupOfEventDialog");
dialog.show();
}
private EventDTO getEventForCourseArea(final List<EventDTO> existingEvents, final UUID courseAreaId) {
EventDTO result = null;
eventLoop:
for (final EventDTO event : existingEvents) {
if (event.venue != null) {
for (CourseAreaDTO courseArea : event.venue.getCourseAreas()) {
if (courseArea.id.equals(courseAreaId)) {
result = event;
break eventLoop;
}
}
}
}
return result;
}
private void createNewRegatta(final RegattaDTO newRegatta, final List<EventDTO> existingEvents) {
LinkedHashMap<String, SeriesCreationParametersDTO> seriesStructure = new LinkedHashMap<String, SeriesCreationParametersDTO>();
for (SeriesDTO seriesDTO : newRegatta.series) {
SeriesCreationParametersDTO seriesPair = new SeriesCreationParametersDTO(seriesDTO.getFleets(),
@@ -234,6 +298,7 @@ public class RegattaManagementPanel extends SimplePanel implements RegattasDispl
@Override
public void onSuccess(RegattaDTO regatta) {
regattaRefresher.fillRegattas();
openCreateDefaultRegattaLeaderboardDialog(regatta, existingEvents);
}
});
}
@@ -11,6 +11,8 @@ import com.google.gwt.core.shared.GWT;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
@@ -103,6 +105,14 @@ public class RegattaWithSeriesAndFleetsCreateDialog extends RegattaWithSeriesAnd
List<EventDTO> existingEvents, StringMessages stringMessages, DialogCallback<RegattaDTO> callback) {
super(new RegattaDTO(), Collections.<SeriesDTO>emptySet(), existingEvents, stringMessages.addRegatta(), stringMessages.ok(),
stringMessages, new RegattaParameterValidator(stringMessages, existingRegattas), callback);
SeriesDTO series = new SeriesDTO();
series.setName("Default");
series.setMedal(false);
series.setStartsWithZeroScore(false);
series.setSplitFleetContiguousScoring(false);
series.setFirstColumnIsNonDiscardableCarryForward(false);
series.setFleets(Collections.singletonList(new FleetDTO(LeaderboardNameConstants.DEFAULT_FLEET_NAME, 0, null)));
seriesEditor.setValue(Collections.singleton(series));
}
@Override
@@ -210,7 +210,7 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
* was <code>null</code> before, or extended to a later point in time.
*/
private void updateMinMax(RaceTimesInfoDTO newRaceTimesInfo) {
Util.Pair<Date, Date> raceMinMax = RaceTimesCalculationUtil.caluclateRaceMinMax(timer, newRaceTimesInfo);
Util.Pair<Date, Date> raceMinMax = RaceTimesCalculationUtil.calculateRaceMinMax(timer, newRaceTimesInfo);
Date min = raceMinMax.getA();
Date max = raceMinMax.getB();
@@ -7,12 +7,12 @@ import com.sap.sse.common.Util;
import com.sap.sse.gwt.client.player.Timer;
public class RaceTimesCalculationUtil {
public static final long MIN_TIME_BEFORE_RACE_START = 5 * 60 * 1000; // 5 minutes
public static final long MIN_TIME_BEFORE_RACE_START = 3 * 60 * 1000; // 3 minutes
private static final long MIN_TIME_AFTER_RACE_START = 5 * 60 * 1000; // 5 minutes
private static final long TIME_AFTER_LIVE = 5 * 60 * 1000; // 5 minutes
public static final long MAX_TIME_AFTER_RACE_END = 5 * 60 * 1000; // 5 minutes
public static final long MAX_TIME_AFTER_RACE_END = 1 * 60 * 1000; // 1 minutes
public static Util.Pair<Date, Date> caluclateRaceMinMax(Timer timer, RaceTimesInfoDTO raceTimesInfo) {
public static Util.Pair<Date, Date> calculateRaceMinMax(Timer timer, RaceTimesInfoDTO raceTimesInfo) {
Date min = null;
Date max = null;
@@ -1468,9 +1468,11 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c
String bufferLineStrokeWeight();
String profile();
String showOnlyCompetitorsOfLog();
String maneuverType();
String confirmLosingCompetitorEditsWhenTogglingLogBasedView();
String removalOfMarkDisabledMayBeUsedInRaces(String raceNames);
String pleaseCreateAtLeastOneMappingBy();
String createDefaultLeaderboardGroup();
String matcherType();
String matchingDevices();
String addDevice();
@@ -1479,4 +1481,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c
String create();
String thereIsAlreadyAConfigurationForThisDevice();
String enterDeviceIdentifierName();
String pleaseSelectALeaderboardGroup();
String linkLeaderboardToLeaderboardGroupOfEvent();
String doYouWantToLinkLeaderboardToLeaderboardGroupOfEvent(String leaderboardName, String eventName);
String failedToLinkLeaderboardToLeaderboardGroup(String leaderboardName, String leaderboardGroupName);
}
@@ -7,6 +7,7 @@ general=General
listRaces=List Races
listRegattas=List Regattas
wind=Wind
maneuverType=Maneuver
windPanelLabel=This is the Wind Panel which so far is completely empty.
refresh=Refresh
remove=Remove
@@ -1437,6 +1438,8 @@ profile=Profile
showOnlyCompetitorsOfLog=Show only competitors registered in Log
confirmLosingCompetitorEditsWhenTogglingLogBasedView=This will reset your current, unsaved changes to the competitors.\nDo you really want to continue?
removalOfMarkDisabledMayBeUsedInRaces=Removal deavticated, marks are used in races {0}
pleaseCreateAtLeastOneMappingBy=Plase create at list one mapping by first selecting a track on the left hand side and then selecting a mark or competitor on the right hand side
createDefaultLeaderboardGroup=Would you like to create a default LeaderboardGroup for your event?
pleaseCreateAtLeastOneMappingBy=Please create at least one mapping by first selecting a track on the left hand side and then selecting a mark or competitor on the right hand side
matcherType=Matcher type
matchingDevices=Matching devices
@@ -1446,3 +1449,7 @@ forWhichDeviceShouldConfigurationApply=Specify for which device the new configur
create=Create
thereIsAlreadyAConfigurationForThisDevice=There is already a configuration for this device.
enterDeviceIdentifierName=Enter an identifier name
pleaseSelectALeaderboardGroup=Please select a leaderboard group
linkLeaderboardToLeaderboardGroupOfEvent=Link Leaderboard to Leaderboard Group of Event
doYouWantToLinkLeaderboardToLeaderboardGroupOfEvent=Link leaderboard {0} to a leaderboard group of event {1}
failedToLinkLeaderboardToLeaderboardGroup=Failed to link leaderboard {0} to leaderboard group {1}
@@ -81,6 +81,7 @@ leaderboard=Ergebnisse
leaderboards=Ranglisten
leaderboardSettings=Einstellungen Rangliste
settings=Einstellungen
maneuverType=Manöver
selectAtLeastOneLegDetail=Mindestens ein Detail auswählen
currentSpeedOverGroundInKnots=FüG
currentSpeedOverGroundInKnotsTooltip=Die aktuelle Fahrt über Grund
@@ -1429,6 +1430,7 @@ scoringSchemeHighPointFirstGetsTwelveOrEightDescription=Scores als Punkte. Der G
scoringSchemeHighPointEssOverall12=High Point System ESS Gesamtwertung (12)
scoringSchemeHighPointEssOverall12Description=High Point System, der Gewinner eines Acts erhält 12 Punkte, der Zweite 11 und so weiter.
pleaseCreateAtLeastOneMappingBy=Bitte erstellen Sie mindestens eine Zuordnung, indem Sie zunächst einen Track auf der linken Seite und anschließend einen Teilnehmer oder eine Tonne auf der rechten Seite auswählen
createDefaultLeaderboardGroup=Möchten Sie eine Default-Ranglisten-Gruppe für ihr Event anlegen?
matcherType=Matcher-Typ
matchingDevices=Passende Geräte
addDevice=Gerät hinzufügen
@@ -1437,3 +1439,7 @@ forWhichDeviceShouldConfigurationApply=Für welches Gerät soll die neue Konfigu
create=Anlegen
thereIsAlreadyAConfigurationForThisDevice=Es gibt bereits eine Konfiguration für dieses Gerät.
enterDeviceIdentifierName=Geräteidentifikator eingeben
pleaseSelectALeaderboardGroup=Bitte eine Ranglisten-Gruppe auswählen
linkLeaderboardToLeaderboardGroupOfEvent=Ergebnisliste einer Ranglisten-Gruppe einer Veranstaltung hinzufügen
doYouWantToLinkLeaderboardToLeaderboardGroupOfEvent=Ergebnisliste {0} zu einer Ranglisten-Gruppe der Veranstaltung {1} hinzufügen
failedToLinkLeaderboardToLeaderboardGroup=Ergebnisliste {0} konnte nicht in Ranglisten-Gruppe {1} eingefügt werden
@@ -7,6 +7,7 @@ trackedBefore=Ранее Отслеженные
general=Всенаро́дный
listRaces=Перечень Гонок
wind=ветра
maneuverType=Maneuver
windPanelLabel=Это окно информации о ветре, которое до сих пор абсолютно пустое.
refresh=Обновить
remove=Удалить
@@ -1359,6 +1360,8 @@ scoringSchemeHighPointFirstGetsTwelveOrEight=High Point, winner gets 12 points (
scoringSchemeHighPointFirstGetsTwelveOrEightDescription=Score in points. Winner scores 12 points, 2nd - 11 points, .... A tie is broken in favour of the competitor having won most races, if tie remains then tie is broken by the rank in the last race scored.
scoringSchemeHighPointEssOverall12=High Point System ESS Overall (12)
scoringSchemeHighPointEssOverall12Description=Score in points. Winner of an act scores 12 points, 2nd - 11 points, .... Any tie in the overall Extreme Sailing Series is broken in favour of the competitor with the most Act wins. If that does not break the tie, the result in the last Act will be used.
pleaseCreateAtLeastOneMappingBy=Plase create at list one mapping by first selecting a track on the left hand side and then selecting a mark or competitor on the right hand side
createDefaultLeaderboardGroup=Would you like to create a default LeaderboardGroup for your event?
pleaseCreateAtLeastOneMappingBy=Please create at least one mapping by first selecting a track on the left hand side and then selecting a mark or competitor on the right hand side
matcherType=Matcher type
matchingDevices=Matching devices
@@ -1368,3 +1371,7 @@ forWhichDeviceShouldConfigurationApply=Specify for which device the new configur
create=Create
thereIsAlreadyAConfigurationForThisDevice=There is already a configuration for this device.
enterDeviceIdentifierName=Enter an identifier name
pleaseSelectALeaderboardGroup=Please select a leaderboard group
linkLeaderboardToLeaderboardGroupOfEvent=Link Leaderboard to Leaderboard Group of Event
doYouWantToLinkLeaderboardToLeaderboardGroupOfEvent=Link leaderboard {0} to a leaderboard group of event {1}
failedToLinkLeaderboardToLeaderboardGroup=Failed to link leaderboard {0} to leaderboard group {1}
@@ -7,6 +7,7 @@ pleaseSelectAnItemToMapTo=Please select an item to map the device to
inEvent=in event
pleaseSelectARace=Please select a race
columnMoveUp=Up
maneuverType=Maneuver
enterCarryValueFor=Enter carried points for competitor {0}
eventOfficialWebsiteURL=Official Website URL
eventSailorsInfoWebsiteURL=Sailors Info Website URL
@@ -1374,6 +1375,8 @@ scoringSchemeHighPointFirstGetsTwelveOrEight=High Point, winner gets 12 points (
scoringSchemeHighPointFirstGetsTwelveOrEightDescription=Score in points. Winner scores 12 points, 2nd - 11 points, .... A tie is broken in favour of the competitor having won most races, if tie remains then tie is broken by the rank in the last race scored.
scoringSchemeHighPointEssOverall12=High Point System ESS Overall (12)
scoringSchemeHighPointEssOverall12Description=Score in points. Winner of an act scores 12 points, 2nd - 11 points, .... Any tie in the overall Extreme Sailing Series is broken in favour of the competitor with the most Act wins. If that does not break the tie, the result in the last Act will be used.
pleaseCreateAtLeastOneMappingBy=Plase create at list one mapping by first selecting a track on the left hand side and then selecting a mark or competitor on the right hand side
createDefaultLeaderboardGroup=Would you like to create a default LeaderboardGroup for your event?
pleaseCreateAtLeastOneMappingBy=Please create at least one mapping by first selecting a track on the left hand side and then selecting a mark or competitor on the right hand side
matcherType=Matcher type
matchingDevices=Matching devices
@@ -1383,3 +1386,7 @@ forWhichDeviceShouldConfigurationApply=Specify for which device the new configur
create=Create
thereIsAlreadyAConfigurationForThisDevice=There is already a configuration for this device.
enterDeviceIdentifierName=Enter an identifier name
pleaseSelectALeaderboardGroup=Please select a leaderboard group
linkLeaderboardToLeaderboardGroupOfEvent=Link Leaderboard to Leaderboard Group of Event
doYouWantToLinkLeaderboardToLeaderboardGroupOfEvent=Link leaderboard {0} to a leaderboard group of event {1}
failedToLinkLeaderboardToLeaderboardGroup=Failed to link leaderboard {0} to leaderboard group {1}
@@ -229,8 +229,6 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
playSpeedBox = new IntegerBox();
playSpeedBox.setVisibleLength(3);
playSpeedBox.setWidth("25px");
playSpeedBox.setHeight("14px");
playSpeedBox.setValue((int)timer.getPlaySpeedFactor()); // Christopher: initialize play speed box according to play speed factor
playSpeedBox.setTitle(stringMessages.playSpeedHelp());
playSpeedBox.addValueChangeHandler(new ValueChangeHandler<Integer>() {
@@ -271,6 +269,7 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
playSpeedImage.getElement().getStyle().setFloat(Style.Float.LEFT);
playSpeedImage.getElement().getStyle().setPadding(3, Style.Unit.PX);
playSpeedImage.getElement().getStyle().setMarginRight(3, Style.Unit.PX);
playSpeedBox.getElement().getStyle().setFloat(Style.Float.LEFT);
playSpeedBox.getElement().getStyle().setPadding(2, Style.Unit.PX);
@@ -143,11 +143,7 @@ public class BoatOverlay extends CanvasOverlayV3 {
}
public void setBoatFix(GPSFixDTO boatFix, long timeForPositionTransitionMillis) {
if (timeForPositionTransitionMillis == -1) {
removeCanvasPositionAndRotationTransition();
} else {
setCanvasPositionAndRotationTransition(timeForPositionTransitionMillis);
}
updateTransition(timeForPositionTransitionMillis);
this.boatFix = boatFix;
}
@@ -111,11 +111,7 @@ public class CompetitorInfoOverlay extends CanvasOverlayV3 {
*/
public void setPosition(Position position, long timeForPositionTransitionMillis) {
if (timeForPositionTransitionMillis == -1) {
removeCanvasPositionAndRotationTransition();
} else {
setCanvasPositionAndRotationTransition(timeForPositionTransitionMillis);
}
updateTransition(timeForPositionTransitionMillis);
this.position = position;
}
@@ -20,7 +20,6 @@ import com.sap.sse.common.Util;
* A google map overlay based on a HTML5 canvas for drawing course marks (canvases) and the buoy zone if the mark is a buoy.
*/
public class CourseMarkOverlay extends CanvasOverlayV3 {
/**
* The course mark to draw
*/
@@ -167,7 +166,8 @@ public class CourseMarkOverlay extends CanvasOverlayV3 {
return mark;
}
public void setMarkPosition(Position position) {
public void setMarkPosition(Position position, long transitionTimeInMillis) {
updateTransition(transitionTimeInMillis);
this.position = position;
}
@@ -0,0 +1,12 @@
package com.sap.sailing.gwt.ui.client.shared.racemap;
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
public enum ManeuverColor {
RED, GREEN;
public static ManeuverColor getManeuverColor(ManeuverDTO maneuver) {
return maneuver.directionChangeInDegrees < 0 ? RED : GREEN;
}
}
@@ -0,0 +1,34 @@
package com.sap.sailing.gwt.ui.client.shared.racemap;
import com.google.gwt.maps.client.base.LatLng;
import com.google.gwt.maps.client.events.click.ClickMapEvent;
import com.google.gwt.maps.client.events.click.ClickMapHandler;
import com.google.gwt.maps.client.overlays.InfoWindow;
import com.google.gwt.maps.client.overlays.InfoWindowOptions;
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
public class ManeuverMarkerClickedListener implements ClickMapHandler {
private RaceMap racemap;
private ManeuverDTO maneuver;
public ManeuverMarkerClickedListener(RaceMap racemap, ManeuverDTO maneuver) {
this.racemap = racemap;
this.maneuver = maneuver;
}
@Override
public void onEvent(ClickMapEvent event) {
final InfoWindow lastInfoWindow = racemap.getLastInfoWindow();
if (lastInfoWindow != null) {
lastInfoWindow.close();
}
LatLng where = racemap.getCoordinateSystem().toLatLng(maneuver.position);
InfoWindowOptions options = InfoWindowOptions.newInstance();
InfoWindow infoWindow = InfoWindow.newInstance(options);
infoWindow.setContent(racemap.getInfoWindowContent(maneuver));
infoWindow.setPosition(where);
racemap.setLastInfoWindow(infoWindow);
infoWindow.open(racemap.getMap());
}
}
@@ -21,7 +21,9 @@ import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
import com.google.gwt.maps.client.LoadApi;
import com.google.gwt.maps.client.LoadApi.LoadLibrary;
import com.google.gwt.maps.client.MapOptions;
@@ -74,7 +76,6 @@ import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
@@ -89,6 +90,7 @@ import com.sap.sailing.gwt.ui.actions.GetWindInfoAction;
import com.sap.sailing.gwt.ui.client.ClientResources;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
import com.sap.sailing.gwt.ui.client.ManeuverTypeFormatter;
import com.sap.sailing.gwt.ui.client.NumberFormatterFactory;
import com.sap.sailing.gwt.ui.client.RaceTimesInfoProviderListener;
import com.sap.sailing.gwt.ui.client.RequiresDataInitialization;
@@ -116,6 +118,7 @@ import com.sap.sailing.gwt.ui.shared.WaypointDTO;
import com.sap.sailing.gwt.ui.shared.WindDTO;
import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO;
import com.sap.sailing.gwt.ui.shared.WindTrackInfoDTO;
import com.sap.sailing.gwt.ui.shared.racemap.CanvasOverlayV3;
import com.sap.sailing.gwt.ui.shared.racemap.GoogleMapAPIKey;
import com.sap.sailing.gwt.ui.shared.racemap.GoogleMapStyleHelper;
import com.sap.sailing.gwt.ui.shared.racemap.RaceSimulationOverlay;
@@ -707,13 +710,16 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
private void removeTransitions() {
// remove the canvas animations for boats
for (BoatOverlay boatOverlay : RaceMap.this.getBoatOverlays().values()) {
for (CanvasOverlayV3 boatOverlay : RaceMap.this.getBoatOverlays().values()) {
boatOverlay.removeCanvasPositionAndRotationTransition();
}
// remove the canvas animations for the info overlays of the selected boats
for (CompetitorInfoOverlay infoOverlay : competitorInfoOverlays.values()) {
infoOverlay.removeCanvasPositionAndRotationTransition();
}
for (CourseMarkOverlay markOverlay : courseMarkOverlays.values()) {
markOverlay.removeCanvasPositionAndRotationTransition();
}
// remove the advantage line animation
if (advantageTimer != null) {
advantageTimer.removeAnimation();
@@ -950,7 +956,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
updateCoordinateSystemFromSettings();
}
// Do mark specific actions
showCourseMarksOnMap(raceMapDataDTO.coursePositions);
showCourseMarksOnMap(raceMapDataDTO.coursePositions, transitionTimeInMillis);
showCourseSidelinesOnMap(raceMapDataDTO.courseSidelines);
showStartAndFinishAndCourseMiddleLines(raceMapDataDTO.coursePositions);
showStartLineToFirstMarkTriangle(raceMapDataDTO.coursePositions);
@@ -1076,7 +1082,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
}
}
private void showCourseMarksOnMap(CoursePositionsDTO courseDTO) {
private void showCourseMarksOnMap(CoursePositionsDTO courseDTO, long transitionTimeInMillis) {
if (map != null && courseDTO != null) {
WaypointDTO endWaypointForCurrentLegNumber = null;
if(courseDTO.currentLegNumber > 0 && courseDTO.currentLegNumber <= courseDTO.totalLegsCount) {
@@ -1100,7 +1106,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
markDTOs.put(markDTO.getName(), markDTO);
courseMarkOverlay.addToMap();
} else {
courseMarkOverlay.setMarkPosition(markDTO.position);
courseMarkOverlay.setMarkPosition(markDTO.position, transitionTimeInMillis);
courseMarkOverlay.setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE));
courseMarkOverlay.setBuoyZoneRadiusInMeter(settings.getBuoyZoneRadiusInMeters());
courseMarkOverlay.setSelected(isSelected);
@@ -1267,7 +1273,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
}
if (!updateTailsOnly) {
for (CompetitorDTO unusedBoatCanvasCompetitorDTO : competitorDTOsOfUnusedBoatCanvases) {
BoatOverlay boatCanvas = boatOverlays.get(unusedBoatCanvasCompetitorDTO);
CanvasOverlayV3 boatCanvas = boatOverlays.get(unusedBoatCanvasCompetitorDTO);
boatCanvas.removeFromMap();
boatOverlays.remove(unusedBoatCanvasCompetitorDTO);
}
@@ -2003,6 +2009,27 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
return vPanel;
}
public Widget getInfoWindowContent(ManeuverDTO maneuver) {
SpeedWithBearingDTO before = maneuver.speedWithBearingBefore;
SpeedWithBearingDTO after = maneuver.speedWithBearingAfter;
VerticalPanel vPanel = new VerticalPanel();
vPanel.add(createInfoWindowLabelAndValue(stringMessages.maneuverType(),
ManeuverTypeFormatter.format(maneuver.type, stringMessages)));
vPanel.add(createInfoWindowLabelAndValue(stringMessages.time(),
DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.timepoint)));
vPanel.add(createInfoWindowLabelAndValue(stringMessages.directionChange(),
((int) maneuver.directionChangeInDegrees) + " " + stringMessages.degreesShort() + " ("
+ ((int) before.bearingInDegrees) + " " + stringMessages.degreesShort() + " -> "
+ ((int) after.bearingInDegrees) + " " + stringMessages.degreesShort() + ")"));
vPanel.add(createInfoWindowLabelAndValue(stringMessages.speedChange(),
NumberFormat.getDecimalFormat().format(after.speedInKnots - before.speedInKnots) + " "
+ stringMessages.knotsUnit() + " ("
+ NumberFormat.getDecimalFormat().format(before.speedInKnots) + " " + stringMessages.knotsUnit()
+ " -> " + NumberFormat.getDecimalFormat().format(after.speedInKnots) + " "
+ stringMessages.knotsUnit() + ")"));
return vPanel;
}
private Widget getInfoWindowContent(WindSource windSource, WindTrackInfoDTO windTrackInfoDTO) {
WindDTO windDTO = windTrackInfoDTO.windFixes.get(0);
NumberFormat numberFormat = NumberFormat.getFormat("0.0");
@@ -2169,21 +2196,27 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
List<ManeuverDTO> maneuversForCompetitor = maneuvers.get(competitorDTO);
for (ManeuverDTO maneuver : maneuversForCompetitor) {
if (settings.isShowManeuverType(maneuver.type)) {
LatLng latLng = coordinateSystem.toLatLng(maneuver.position);
Marker maneuverMarker = raceMapImageManager.maneuverIconsForTypeAndTargetTack.get(new com.sap.sse.common.Util.Pair<ManeuverType, Tack>(maneuver.type, maneuver.newTack));
MarkerOptions options = MarkerOptions.newInstance();
options.setTitle(maneuver.toString(stringMessages));
options.setIcon(maneuverMarker.getIcon_MarkerImage());
Marker marker = Marker.newInstance(options);
marker.setPosition(latLng);
maneuverMarkers.add(marker);
marker.setMap(map);
createAndAddMarkerOfManeuver(maneuver);
}
}
}
}
}
private void createAndAddMarkerOfManeuver(ManeuverDTO maneuver) {
LatLng latLng = coordinateSystem.toLatLng(maneuver.position);
Marker maneuverMarker = raceMapImageManager.maneuverIconsForTypeAndDirectionIndicatingColor
.get(new Pair<ManeuverType, ManeuverColor>(maneuver.type, ManeuverColor.getManeuverColor(maneuver)));
MarkerOptions options = MarkerOptions.newInstance();
options.setIcon(maneuverMarker.getIcon_MarkerImage());
Marker marker = Marker.newInstance(options);
marker.setPosition(latLng);
marker.setTitle(ManeuverTypeFormatter.format(maneuver.type, stringMessages));
marker.addClickHandler(new ManeuverMarkerClickedListener(this, maneuver));
maneuverMarkers.add(marker);
marker.setMap(map);
}
/**
* @param date
* the point in time for which to determine the competitor's boat position; approximated by using the fix
@@ -2270,7 +2303,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
while (i.hasNext()) {
Entry<CompetitorDTO, BoatOverlay> next = i.next();
if (!next.getKey().equals(competitor)) {
BoatOverlay boatOverlay = next.getValue();
CanvasOverlayV3 boatOverlay = next.getValue();
boatOverlay.removeFromMap();
fixesAndTails.removeTail(next.getKey());
i.remove(); // only this way a ConcurrentModificationException while looping can be avoided
@@ -2749,4 +2782,16 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
protected String getLeftControlsIndentStyle() {
return null;
}
DelegateCoordinateSystem getCoordinateSystem() {
return coordinateSystem;
}
void setLastInfoWindow(InfoWindow infoWindow) {
lastInfoWindow = infoWindow;
}
InfoWindow getLastInfoWindow() {
return lastInfoWindow;
}
}
@@ -1,21 +1,27 @@
package com.sap.sailing.gwt.ui.client.shared.racemap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.gwt.core.client.GWT;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.base.Point;
import com.google.gwt.maps.client.base.Size;
import com.google.gwt.maps.client.overlays.Marker;
import com.google.gwt.maps.client.overlays.MarkerImage;
import com.google.gwt.maps.client.overlays.MarkerOptions;
import com.google.gwt.resources.client.ImageResource;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.Tack;
import com.sap.sse.common.Util;
public class RaceMapImageManager {
private static final int MARKER_HEIGHT = 26;
private static final int MARKER_WIDTH = 26;
/**
* An arrow showing the combined wind depending on the wind direction
*/
@@ -31,49 +37,44 @@ public class RaceMapImageManager {
*/
protected ImageTransformer windSensorIconTransformer;
protected Map<Util.Pair<ManeuverType, Tack>, Marker> maneuverIconsForTypeAndTargetTack;
protected Map<Util.Pair<ManeuverType, ManeuverColor>, Marker> maneuverIconsForTypeAndDirectionIndicatingColor;
private static RaceMapResources resources = GWT.create(RaceMapResources.class);
public RaceMapImageManager() {
maneuverIconsForTypeAndTargetTack = new HashMap<Util.Pair<ManeuverType, Tack>, Marker>();
maneuverIconsForTypeAndDirectionIndicatingColor = new HashMap<Util.Pair<ManeuverType, ManeuverColor>, Marker>();
trueNorthIndicatorIconTransformer = new ImageTransformer(resources.trueNorthIndicatorIcon());
combinedWindIconTransformer = new ImageTransformer(resources.combinedWindIcon());
windSensorIconTransformer = new ImageTransformer(resources.expeditionWindIcon());
}
/**
* Loads the map overlay icons The method can only be called after the map is loaded. The {@link #maneuverIconsForTypeAndTargetTack} map
* is filled for all combinations of {@link ManeuverType maneuver types} and {@link Tack tacks} including the value <code>null</code> representing
* the unknown tack.
* Loads the map overlay icons The method can only be called after the map is loaded. The {@link #maneuverIconsForTypeAndDirectionIndicatingColor} map
* is filled for all combinations of {@link ManeuverType maneuver types} and {@link ManeuverColor colors}.
*/
public void loadMapIcons(MapWidget map) {
if (map != null) {
final Map<ManeuverType, Character> maneuverLetter = new HashMap<>();
maneuverLetter.put(ManeuverType.TACK, 'T');
maneuverLetter.put(ManeuverType.JIBE, 'J');
maneuverLetter.put(ManeuverType.HEAD_UP, 'H');
maneuverLetter.put(ManeuverType.BEAR_AWAY, 'B');
maneuverLetter.put(ManeuverType.MARK_PASSING, 'M');
maneuverLetter.put(ManeuverType.PENALTY_CIRCLE, 'P');
maneuverLetter.put(ManeuverType.UNKNOWN, '?');
final Map<Tack, String> tackColor = new HashMap<>();
tackColor.put(Tack.STARBOARD, "71bf44");
tackColor.put(Tack.PORT, "d95252");
tackColor.put(null, "FFFFFF");
for (Entry<ManeuverType, Character> m : maneuverLetter.entrySet()) {
for (Entry<Tack, String> t : tackColor.entrySet()) {
final Marker icon = createMarker("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld="+m.getValue()+"|"+t.getValue()+"|000000", 10, 33);
maneuverIconsForTypeAndTargetTack.put(new Util.Pair<ManeuverType, Tack>(m.getKey(), t.getKey()), icon);
}
final List<ManeuverType> maneuvers = new ArrayList<ManeuverType>();
for (ManeuverType type : ManeuverType.values()) {
maneuvers.add(type);
}
Marker icon;
for (ManeuverType maneuver : maneuvers) {
icon = createMarker(resources.maneuverMarkerRed());
maneuverIconsForTypeAndDirectionIndicatingColor
.put(new Util.Pair<ManeuverType, ManeuverColor>(maneuver, ManeuverColor.RED), icon);
icon = createMarker(resources.maneuverMarkerGreen());
maneuverIconsForTypeAndDirectionIndicatingColor
.put(new Util.Pair<ManeuverType, ManeuverColor>(maneuver, ManeuverColor.GREEN), icon);
}
}
}
private Marker createMarker(String iconUrl, int anchorX, int anchorY) {
private Marker createMarker(ImageResource ressource) {
MarkerOptions options = MarkerOptions.newInstance();
MarkerImage markerImage = MarkerImage.newInstance(iconUrl);
markerImage.setAnchor(Point.newInstance(anchorX, anchorY));
MarkerImage markerImage = MarkerImage.newInstance(ressource.getSafeUri().asString());
markerImage.setAnchor(Point.newInstance(MARKER_WIDTH/2, MARKER_HEIGHT/2));
markerImage.setScaledSize(Size.newInstance(MARKER_WIDTH, MARKER_HEIGHT));
options.setIcon(markerImage);
Marker marker = Marker.newInstance(options);
return marker;
@@ -21,7 +21,13 @@ public interface RaceMapResources extends ClientBundle {
@Source("com/sap/sailing/gwt/ui/client/images/wind_expedition.png")
ImageResource expeditionWindIcon();
@Source("com/sap/sailing/gwt/ui/client/images/maneuver_red.png")
ImageResource maneuverMarkerRed();
@Source("com/sap/sailing/gwt/ui/client/images/maneuver_green.png")
ImageResource maneuverMarkerGreen();
@Source("CombinedWindPanel.css")
CombinedWindPanelStyle combinedWindPanelStyle();
}
@@ -51,8 +51,7 @@ public class RaceMapSettings extends AbstractSettings {
private boolean windUp = false;
public RaceMapSettings() {
// empty default settings; don't show maneuvers by default
maneuverTypesToShow = new HashSet<ManeuverType>();
this.maneuverTypesToShow = getDefaultManeuvers();
this.zoomSettings = new RaceMapZoomSettings();
this.helpLinesSettings = new RaceMapHelpLinesSettings();
}
@@ -217,7 +216,15 @@ public class RaceMapSettings extends AbstractSettings {
public void setShowMapControls(boolean showMapControls) {
this.showMapControls = showMapControls;
}
private HashSet<ManeuverType> getDefaultManeuvers() {
HashSet<ManeuverType> types = new HashSet<ManeuverType>();
types.add(ManeuverType.JIBE);
types.add(ManeuverType.TACK);
types.add(ManeuverType.PENALTY_CIRCLE);
return types;
}
public static RaceMapSettings readSettingsFromURL() {
final boolean showMapControls = GwtHttpRequestUtils.getBooleanParameter(PARAM_SHOW_MAPCONTROLS, true /* default */);
return new RaceMapSettings(showMapControls);
@@ -134,11 +134,7 @@ public class SmallTransparentInfoOverlay extends CanvasOverlayV3 {
*/
public void setPosition(Position position, long timeForPositionTransitionMillis) {
if (timeForPositionTransitionMillis == -1) {
removeCanvasPositionAndRotationTransition();
} else {
setCanvasPositionAndRotationTransition(timeForPositionTransitionMillis);
}
updateTransition(timeForPositionTransitionMillis);
this.position = position;
}
@@ -408,4 +408,12 @@ public abstract class CanvasOverlayV3 {
public MapCanvasProjection getMapProjection() {
return mapProjection;
}
protected void updateTransition(long timeForPositionTransitionMillis) {
if (timeForPositionTransitionMillis == -1) {
removeCanvasPositionAndRotationTransition();
} else {
setCanvasPositionAndRotationTransition(timeForPositionTransitionMillis);
}
}
}
@@ -1,461 +1,470 @@
<!DOCTYPE html>
<html>
<body>
<div id="mainContent">
<h4 class="articleHeadline">What's New - SAP Sailing Analytics</h4>
<div class="innerContent">
<h5 class="articleSubheadline">February 2016</h5>
<ul class="bulletList">
<li>The website now features a user registration and authentication interface.
User accounts are currently used to grant special permissions to administrators and moderators.
New user specific features (e.g. preferences) will be available in the next couple of months.
</li>
<li>The RaceBoard URL Parameter "canReplayDuringLiveRaces" has been removed.
This parameter was used to allow users to trigger autoplay for live races on non-live points in time.
The permission "CAN_REPLAY_DURING_LIVE_RACES" and role "moderator" now grant this functionality to users.
</li>
</ul>
<h5 class="articleSubheadline">December 2015</h5>
<ul class="bulletList">
<li>The advantage line is now animated and moves smoothly with the boats.
</li>
<li>New line hover behavior in race map. When the mouse is moved onto a line, for example the start line, a transparent,
wider line is activated behind it that acts as a buffer for the thin line, where the text displayed by the hover is still shown.
This makes it easier to hover over the lines. These wider lines can be made semi transparent instead of transparent
by unticking the corresponding checkbox in the settings of the race map. Furthermore the stroke weight of the wider lines can be
configured in the same settings dialog.
</li>
<li>The race viewer (RaceBoard.html) now intelligently chooses a layout that suits the screen size.
When only little horizontal space is available, only the sail number and nationality but not the
competitor's long name is displayed by default. When vertical space is scarce, competitor and wind
charts are disabled and more horizontal space can be given to the time slider at the bottom. The
leaderboard section can then be scaled to smaller widths and will reduce the column widths better
than in the past.
</li>
<li>With the changes described above, the RaceBoard is now also accessible from the races page on mobile devices.
</li>
<li>For overall / series leaderboards such as a national sailing league or the Extreme Sailing Series, the
live status of the overall leaderboard is now indicated. As long as a live race is ongoing, the respective
column in the overall leaderboard is highlighted, and a comment is displayed that indicates which regatta
is currently still live.
</li>
</ul>
<h5 class="articleSubheadline">November 2015</h5>
<ul class="bulletList">
<li>The smartphone page provides some structural navigation items for several pages in the upper right burger menu.
You will see links to e.g. events, regattas and series when navigating on event and series pages.
</li>
<li>It is now possible to filter for competitors in the race list of events/regattas.
If competitor information is available, there is a filter box in the upper right corner of the races page.
The filter is searching for competitors by sail ID as well as the competitor's name.
</li>
<li>Regattas can now be filtered by boat category in the smartphone page.
The filter will be shown for events with multiple regattas if there is more than on boat category associated with the event.
</li>
</ul>
<h5 class="articleSubheadline">October 2015</h5>
<ul class="bulletList">
<li>A variety of tablets will now show the desktop page by default.
The mobile version of the website is primarily designed to be used on smartphones,
but you can manually switch back to the mobile version by clicking the corresponding link in the page footer.
</li>
<li>There is some amount of caching on the web page to reduce the consumed traffic for mobile users.
Contents aren't loaded again on each page switch. This improves the traffic consumption and loading speed on fast page switches.
</li>
<li>The fullscreen image viewer (start page and event media page) can automatically slide through the images.
This can be turned on by clicking the corresponding icon in the upper right corner of the fullscreen viewer.
</li>
<li>There is an optimized search and results page for mobile devices now.
</li>
</ul>
<h5 class="articleSubheadline">September 2015</h5>
<ul class="bulletList">
<li>Races in the event/regatta race list can now be shown in 'competition format'.
In this format the races and flights are shown in a structural view.
In contrast to the default list view there is no grouping by live and finished states and all
(including upcoming) races/flights are shown in one structural view.
</li>
<li>The mobile page now provides regatta overview pages for events having multiple regattas.
This page shows live races, an excerpt of the current results as well as statistics for the regatta.
</li>
</ul>
<h5 class="articleSubheadline">August 2015</h5>
<ul class="bulletList">
<li>Series events appear as only one entry in the events list, with a marker that
this is only one of a certain number of events in a series. This way, the many series events
don't produce clutter in the events list.
</li>
<li>Bug fix: memory leaks for distance cache have been solved. The issue could
also lead to endless recursions, ultimately leading to server lock-ups.
</li>
<li>Bug fix: master data import detached race logs and regatta logs from database.
Log entries added after import on the importing server were not written to the
database and therefore were lost upon server restart.
</li>
<li>There is a smartphone-optimized version of the "solutions" page known from the desktop.
</li>
<li>The smartphone event history now has a slider that allows swiping through the events of a year.
</li>
</ul>
<h5 class="articleSubheadline">July 2015</h5>
<ul class="bulletList">
<li>Major improvements to the web page, with specific enhancements for smartphone
users. Race lists are now sortable by various criteria by clicking on the column
headers. A new fancy progress indicator now also provides visual feedback for
multi-fleet regattas. A news ticker box has been implemented, and the event
overview page can hold a video player on its stage. The event media gallery section
has been greatly improved.
</li>
<li>Fixed a bug in the wind streamlet visualization. Now the wind streamlets
correctly show the wind at the location selected also in wind-up mode.
</li>
<li>Added the "Course Geometry" option to the map display. With this option selected,
the map will show an info text for each leg and the start and finish line (only
for one of start/finish line if they are identical), telling the length of the leg
or line, respectively, and its angle to the wind. For the start and finish line
the advantage of the favored side is shown in meters.
</li>
<li>The SAP Sailing Analytics start page is now available in the smartphone version.
This page features some events that are currently interesting as well as showing a
quickfinder for a variety of recent and upcoming events.
Previously, the start page redirected to the event history.
</li>
</ul>
<h5 class="articleSubheadline">May 2015</h5>
<ul class="bulletList">
<li>Competitors can have a separate flag image, such as a club flag to be displayed instead of
the nationality flag usually displayed with the sail number. This enables national league events
to display the club flags consistently when all nation flags would be the same anyway.
</li>
<li>The wind estimation from the tracked courses over ground has been improved. It now takes into
account any polar data available for the boat class concerned. In particular, this helps to rule
out very unlikely gybing angles in downwind clusters that otherwise could have led to an incorrect
split of the downwind fleet into two clusters where actually all boats were on the same tack.
</li>
<li>Some significant improvements have been applied to the simulator feature which is embedded in the
race viewer since some time now. The simulator now copes with very shifty wind conditions and finds
solutions in scenarios where an upwind leg flips to a reach or even into a downwind leg over time.
</li>
</ul>
<h5 class="articleSubheadline">April 2015</h5>
<ul class="bulletList">
<li>There is a new map setting "Wind-Up (show wind from top of map)". Selecting it will
align the map's top with the combined true wind average direction around the time currently
selected in the time slider. In this new view there are limitations as to how far you can zoom
out. Furthermore, you will not be able to use the Satellite view with this setting. Tip:
if you want to re-align to wind-up after a major wind shift, un-check the setting, confirm
the setting change, then set it again. A compass shows the true north orientation. Clicking
the compass will toggle between wind-up and north-up display.
</li>
<li>Bug fixes around addition of the "Regatta Rank" column in the leaderboard which could have
distorted the leaderboard's column layout under certain conditions.
</li>
<li>Bug fix for cumulative points in the leaderboard ("Show added scores for each entry in leaderboard").
With this setting, scores of competitors not tracked in the current race were not correctly represented
and may have stayed blank.
</li>
<li>Bug fix for the leg counter in the regatta page: when the first boat has finished, the leg counter went
to zero (e.g., "0 of 8"). This has now been fixed, and it will show, e.g., "8 of 8" upon the first boat
crossing the finish line.
</li>
</ul>
<h5 class="articleSubheadline">March 2015</h5>
<ul class="bulletList">
<li>Added competitor chart type for Course Over Ground (COG)
</li>
<li>Added leaderboard column and competitor chart entry for distances considering
gate starts. With gate starts, late starters sail a shorter distance in their first
leg. To still make distances comparable, a new metric has been introduced that for a gate
start adds the distance between the port side of the start line and the position where
the competitor started to the distance sailed in the first leg.
</li>
<li>The weights of the wind measurement units, the wind estimation and manual wind inputs by the
race committee are now used properly for the wind streamlet visualization. This fixes issues
with exaggerated weights for an estimated wind direction of very low confidence.
</li>
<li>The expand/collapse toggle button in the race viewer's time panel has correct arrows now.
</li>
</ul>
<h5 class="articleSubheadline">February 2015</h5>
<ul class="bulletList">
<li>The leaderboard offers a new column: "Start delay." It shows the number of seconds that passed
since the race start and the time the competitor passes the start mark / start line. As usual,
you can sort for this column. For line starts, this is a fairly direct measure of start quality
except for port starts which may have to be considered differently. For gate starts, you can use
this measure to find out about early and late starters and dig into possible correlations between
results and start time point.
</li>
</ul>
<h5 class="articleSubheadline">January 2015</h5>
<ul class="bulletList">
<li>The race viewer ("RaceBoard.html") now offers a new option in the map settings:
"Show simulation overlay". Checking this feature will simulate the current leader's leg
using different strategies and using the wind as it has been measured and estimated.
This feature becomes particularly interesting when combined with the "Show wind streamlets overlay"
feature that visualizes the spatial distribution of the wind field over the course.
</li>
<li>The race viewer is now more resilient against exceptional conditions such as the missing
of all wind information and the missing of a course with its waypoints. If no wind information
is available at all (no wind direction indicator is then shown in the upper left corner of
the map) then all ranking and distances will be based on a projection onto the course instead
of onto the wind direction, even for upwind and downwind legs.
</li>
</ul>
<h5 class="articleSubheadline">September 2014</h5>
<ul class="bulletList">
<li>The search feature now also includes results from currently ongoing events that may not yet be
included in our archive. Search keywords are now properly used in constructing the query.
</li>
</ul>
<h5 class="articleSubheadline">August 2014</h5>
<ul class="bulletList">
<li>The 2D race viewer, also known as the "Race Board," now comes with a fresh, new design. The orange
checkbox toggle buttons that open or close views such as the leaderboard or the charts for wind
and competitor comparison, have been replaced by tabs with "&gt;&gt;" symbols. Clicking on those
will expand or collapse the respective view. The views can now be resized using the handles attached
to the dividers separating the views. They work much better on mobile and touch devices than the splitters
used previously did. The settings buttons that used to be integrated with the orange toggle
checkbox buttons have now been moved into the views that they control. For example, the map settings
button is now displayed on the map, below the zoom control on the right map border.
</li>
<li>The leaderboard now has an improved search and filter control. Above the table displaying the
competitors and their figures is a new field with a text input that you can use to filter for
competitors, by their sail number or their name. While filtered, use the checkboxes in front of
the table row to add the competitor to or remove it from the selection. Use the "X" button to the
right of the search input field to clear the search. To the left of the search input field is a
filter symbol that you can use to select and create advanced filtering rules, such as filtering
by rank or nationality. Left of the filter button is the leaderboard settings button.
</li>
<li>The leaderboard has a new first column: a selection check box. We hope this makes it easier in
particular for users on mobile or other touch devices to select more than one competitor at a time.
The old-style multi-selection using the Control and Shift keys, as known well from typical desktop
file explorers, still work like they did before. Note that it is possible to sort for the checkbox
column, putting the selected competitors at the top of the leaderboard.
</li>
<li>When using the auto-zoom feature from the map settings, boats and wind streamlets will now
continue to move smoothly as long as the map only needs panning. Please also note that auto-zooming
to "tails" may slow down the display considerable, particularly when tracking at high frequency.
</li>
<li>When viewing a single race, the leaderboard now by default does no longer show the regatta rank as
the first column (and not as the second one either, given that the first column now usually shows
the checkboxes used for selecting items). If you'd like to get the regatta rank column back, also when
looking at a single race, please choose it from the leaderboard settings dialog where you can find it
in the "Overall Details" section. It may still be a nice column to have when following the last race
in a regatta. If you're just interested in the relative regatta rank ordering you may as well sort by
the right-most total points column which (surprising to some but welcome by most) sorts by the regatta
rank and not by the plain points sum (taking into account medal race and gold/silver/... fleet participations).
</li>
</ul>
<h5 class="articleSubheadline">July 2014</h5>
<ul class="bulletList">
<li>The advantage line now uses the wind at the position in the middle of the leg that the
competitor to which the line is attached is currently sailing in. Before, the "global" wind
average was used which is still displayed in the top left corner of the map. You can understand
the details by turning on the "Streamlet" wind visualization in the Map Settings.
</li>
<li>Faster wind streamlet loading: when you activate the streamlets for wind visualization, they now
display much faster.
</li>
<li>Faster boat position display: We found that what you're probably the most interested in when
you first open a race map or move the time slider to some very different point in time is the
position of the boats at that point in time; and you want to know it fast.
The tails which show how the boats got there are also interesting, but you won't blame us as much
if it takes a little bit to update them. So we've separated the placement of the boats from the
drawing of their tails. Now you may blame us for the tails temporarily not being attached to the
boats anymore ;-).
</li>
</ul>
<h5 class="articleSubheadline">June 2014</h5>
<ul class="bulletList">
<li>In the "Maps" settings in the RaceBoard page there is a new checkbox named
"Show wind streamlets overlay." If you select it and wait for about a minute,
a complete wind field will be calculated and visualized as an overlay on top
of the race course display. It reacts to you moving the time slider and shows
how the wind differs at different locations on the course. Stronger and faster-moving
streamlets mean stronger breeze.
</li>
<li>Wind is now calculated location-based. At locations closer to a sensor, that sensor
has a higher influence on the local wind field than sensors further away. The wind
estimations based on the courses sailed are assigned a location that corresponds with
the "center of gravity" of the field that was used for the estimation. For example,
if the fleet is approaching the windward mark and the wind sensor in on the committee
boat on the leeward side of the course, the course-based estimation influences the
assumed wind field more on the windward side whereas the sensor counts more for
the leeward side.
</li>
<li>Switched to the latest version of the Highcharts charting framework. This leads to improvements
especially on mobile devices. Charts can be zoomed using pinch gestures now.
</li>
<li>To support the use of the SAP Sailing Analytics also for live video streaming formats, we
introduced a new URL parameter <tt>viewShowMapControls</tt> for the <tt>Spectator.html</tt>
and the <tt>RaceBoard.html</tt> pages. Setting this parameter to "false"
will suppress the zoom, pan and map type selection
controls on the map display. Example:
<a href="http://danishleague2014.sapsailing.com/gwt/Spectator.html?leaderboardGroupName=Danish%20League%202014&showRaceDetails=true&viewShowMapControls=false">http://danishleague2014.sapsailing.com/gwt/Spectator.html?leaderboardGroupName=Danish%20League%202014&showRaceDetails=true&viewShowMapControls=false</a>.
When used for the <tt>Spectator.html</tt> URL, this will apply
to all <tt>RaceBoard.html</tt> pages (all races) reached from there. Of course, the map
can still be panned and zoomed using the usual mouse gestures. A left double click zooms
in, a right double click zooms out. Zoom can also be controlled using the mouse wheel.
And panning works by clicking on the map and dragging the mouse. Without the map type
control, switching to satellite view is not possible (keep this in mind when tracking
races in China, see below for Google's random coordinate transformations there).
</li>
</ul>
<h5 class="articleSubheadline">May 2014</h5>
<ul class="bulletList">
<li>Fixed an issue with the wind speed charts. Charts that only had a single, unchanged
wind speed crashed the browser. The solution scales the right hand side Y-axis denoting
the wind speed such that it always starts at 0kts.
</li>
<li>Analysis of tacks and jibes around mark passings has now been improved. Before,
a jibe followed by a tack that turns in the same direction (e.g., to port) so far
was always called a penalty maneuver. Now, if a mark passing separates such a tack
from such a jibe, no penalty is called anymore, and the two maneuvers are identified
correctly.
</li>
<li>Another note on maneuver analysis: we received questions regarding the maneuvers
involved in clearing an OCS. The SAP Sailing Analytics treat a clearing of
an OCS, going back behind the start line and crossing it again, as if the competitor
started again. In particular this means that all maneuvers prior to this new start
do not count for the metrics; neither does the distance sailed between the first (OCS)
start and the clear start.
</li>
<li>A hint regarding the "distance to start line" metrics: please keep in mind that currently this
measurement is calculated as the distance of the GPS tracker from the line. Unless the tracker
is mounted near the bow (or at the bow sprit, if any), the distance calculated this way will
be greater than what counts for crossing the line. Ordering, comparing and aggregating over this
figure still makes sense as long as you remember this detail.
</li>
<li>Introduced a new leaderboard entry "XTE +/-" featuring a signed cross-track error which shows
for a leg and also aggregated for an entire race on which side of the leg a competitor stayed on
average. Currently, the "side" is defined as viewing in the leg's direction, so "left" for a downwind
leg is the same side as "right" for the corresponding upwind leg.
</li>
</ul>
<h5 class="articleSubheadline">April 2014</h5>
<ul class="bulletList">
<li>China races, such as in Qingdao, may have the map background displayed at slightly wrong locations.
This is due to an intentional random transformation that Google applies to GPS locations for
China. You can circumvent this issue by switching to satellite view where the map background is displayed
correctly. <img src="/images/mapsatellite.png"/>
</li>
<li>Fixed a bug that in some cases has caused the wind chart, the advantage line, the wind indicator on the map
and the leaderboard to use slightly different wind values. The problem was related to different averaging and
damping methods used for these values which has now been aligned.
</li>
<li>Fixed a bug that caused the time spent in a leg, race or regatta to always show the time at the end of the leg
if the leg has been started by the competitor at the time point queried, e.g., by the time slider. While this
did not have any serious effects in live mode, for replay races this left the leg time and race time constant
at the total time spent so far up to the end of the last leg the competitor had started at that time.
</li>
</ul>
<h5 class="articleSubheadline">March 2014</h5>
<ul class="bulletList">
<li>Added a new setting for a leaderboard called "Show added scores". Using this feature one
can display added scores for each leaderboard entry. An added score is the sum over all scores
from the first column to the current column. One can describe it as an intermediate overall score.
This feature can be used to know the score until a specific race while another race is live and
constantly changes the overall score.</li>
<li>Added tooltips for leaderboard settings.</li>
<li>Fixed HighPointOverall scheme to also break a tie by looking at the ranks of the last regatta.</li>
<li>Progress indicator when expanding a race in the RaceBoard view</li>
<li>Leaderboard column "GPS Lag" shows how far a competitor's GPS signal lags behind the average
tracking quality. A background color bar classifies into good/ok/bad (green/yellow/red).</li>
</ul>
<h5 class="articleSubheadline">February 2014</h5>
<ul class="bulletList">
<li>Fixed an issue with boat rotation on Safari browsers</li>
<li>Maneuver analysis now counts tacks and jibes based on wind
at boat position and not based on leg type; subsequently,
penalty circle analysis is now based on a tack and a jibe
in close succession instead of a full circle.</li>
<li>Regatta rank and overall rank in competitor chart of leaderboard panel
has been fixed; it did erroneously display the rank of the particular
race, not the regatta/overall rank after that race.</li>
<li>Regatta overview now also displays all regatta leaderboards</li>
<li>Boat colors match tail colors</li>
<li>When selecting boats in leaderboard, de-selected boats' tails turn grey</li>
</ul>
<h5 class="articleSubheadline">January 2014</h5>
<ul class="bulletList">
<li>Boat movement animated smoothly</li>
<li>Start and Finish line show line parameters (length, angle to wind,
advantage) when mouse hovers over them</li>
<li>Start line has a count-down indicator displayed next to it
before the start. The count-down is also displayed underneath the time
slider</li>
<li>Competitor and Wind charts show pre-start data where possible</li>
<li>Competitors can be assigned a fixed color which they will keep
for all races in a regatta</li>
<li>By default, videos are now shown in a moveable, resizable box embedded in
the web page. For a two screen setup, the embedded box can be popped out to a
dedicated browser window.</li>
<li>Official score corrections for historic races are only applied after
the respective competitor finished the race. Therefore, in-between race
scores now correspond with tracking, regardless of final corrected score.</li>
<li>By using negative playback speed (e.g., -1), replays can be run backwards.</li>
</ul>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<body>
<div id="mainContent">
<h4 class="articleHeadline">What's New - SAP Sailing Analytics</h4>
<div class="innerContent">
<h5 class="articleSubheadline">February 2016</h5>
<ul class="bulletList">
<li>The website now features a user registration and authentication interface.
User accounts are currently used to grant special permissions to administrators and moderators.
New user specific features (e.g. preferences) will be available in the next couple of months.
</li>
<li>The RaceBoard URL Parameter "canReplayDuringLiveRaces" has been removed.
This parameter was used to allow users to trigger autoplay for live races on non-live points in time.
The permission "CAN_REPLAY_DURING_LIVE_RACES" and role "moderator" now grant this functionality to users.
</li>
</ul>
<h5 class="articleSubheadline">January 2016</h2>
<ul class="bulletList">
<li>Show maneuver markers on the race board by default
(tacks, jibes and penalty circles) with a smaller icon, which
shows an info bubble when the marker is clicked.
The icon turns green if the direction change is to starboard
and red if to port.
</li>
</ul>
<h5 class="articleSubheadline">December 2015</h5>
<ul class="bulletList">
<li>The advantage line is now animated and moves smoothly with the boats.
</li>
<li>New line hover behavior in race map. When the mouse is moved onto a line, for example the start line, a transparent,
wider line is activated behind it that acts as a buffer for the thin line, where the text displayed by the hover is still shown.
This makes it easier to hover over the lines. These wider lines can be made semi transparent instead of transparent
by unticking the corresponding checkbox in the settings of the race map. Furthermore the stroke weight of the wider lines can be
configured in the same settings dialog.
</li>
<li>The race viewer (RaceBoard.html) now intelligently chooses a layout that suits the screen size.
When only little horizontal space is available, only the sail number and nationality but not the
competitor's long name is displayed by default. When vertical space is scarce, competitor and wind
charts are disabled and more horizontal space can be given to the time slider at the bottom. The
leaderboard section can then be scaled to smaller widths and will reduce the column widths better
than in the past.
</li>
<li>With the changes described above, the RaceBoard is now also accessible from the races page on mobile devices.
</li>
<li>For overall / series leaderboards such as a national sailing league or the Extreme Sailing Series, the
live status of the overall leaderboard is now indicated. As long as a live race is ongoing, the respective
column in the overall leaderboard is highlighted, and a comment is displayed that indicates which regatta
is currently still live.
</li>
</ul>
<h5 class="articleSubheadline">November 2015</h5>
<ul class="bulletList">
<li>The smartphone page provides some structural navigation items for several pages in the upper right burger menu.
You will see links to e.g. events, regattas and series when navigating on event and series pages.
</li>
<li>It is now possible to filter for competitors in the race list of events/regattas.
If competitor information is available, there is a filter box in the upper right corner of the races page.
The filter is searching for competitors by sail ID as well as the competitor's name.
</li>
<li>Regattas can now be filtered by boat category in the smartphone page.
The filter will be shown for events with multiple regattas if there is more than on boat category associated with the event.
</li>
</ul>
<h5 class="articleSubheadline">October 2015</h5>
<ul class="bulletList">
<li>A variety of tablets will now show the desktop page by default.
The mobile version of the website is primarily designed to be used on smartphones,
but you can manually switch back to the mobile version by clicking the corresponding link in the page footer.
</li>
<li>There is some amount of caching on the web page to reduce the consumed traffic for mobile users.
Contents aren't loaded again on each page switch. This improves the traffic consumption and loading speed on fast page switches.
</li>
<li>The fullscreen image viewer (start page and event media page) can automatically slide through the images.
This can be turned on by clicking the corresponding icon in the upper right corner of the fullscreen viewer.
</li>
<li>There is an optimized search and results page for mobile devices now.
</li>
</ul>
<h5 class="articleSubheadline">September 2015</h5>
<ul class="bulletList">
<li>Races in the event/regatta race list can now be shown in 'competition format'.
In this format the races and flights are shown in a structural view.
In contrast to the default list view there is no grouping by live and finished states and all
(including upcoming) races/flights are shown in one structural view.
</li>
<li>The mobile page now provides regatta overview pages for events having multiple regattas.
This page shows live races, an excerpt of the current results as well as statistics for the regatta.
</li>
</ul>
<h5 class="articleSubheadline">August 2015</h5>
<ul class="bulletList">
<li>Series events appear as only one entry in the events list, with a marker that
this is only one of a certain number of events in a series. This way, the many series events
don't produce clutter in the events list.
</li>
<li>Bug fix: memory leaks for distance cache have been solved. The issue could
also lead to endless recursions, ultimately leading to server lock-ups.
</li>
<li>Bug fix: master data import detached race logs and regatta logs from database.
Log entries added after import on the importing server were not written to the
database and therefore were lost upon server restart.
</li>
<li>There is a smartphone-optimized version of the "solutions" page known from the desktop.
</li>
<li>The smartphone event history now has a slider that allows swiping through the events of a year.
</li>
</ul>
<h5 class="articleSubheadline">July 2015</h5>
<ul class="bulletList">
<li>Major improvements to the web page, with specific enhancements for smartphone
users. Race lists are now sortable by various criteria by clicking on the column
headers. A new fancy progress indicator now also provides visual feedback for
multi-fleet regattas. A news ticker box has been implemented, and the event
overview page can hold a video player on its stage. The event media gallery section
has been greatly improved.
</li>
<li>Fixed a bug in the wind streamlet visualization. Now the wind streamlets
correctly show the wind at the location selected also in wind-up mode.
</li>
<li>Added the "Course Geometry" option to the map display. With this option selected,
the map will show an info text for each leg and the start and finish line (only
for one of start/finish line if they are identical), telling the length of the leg
or line, respectively, and its angle to the wind. For the start and finish line
the advantage of the favored side is shown in meters.
</li>
<li>The SAP Sailing Analytics start page is now available in the smartphone version.
This page features some events that are currently interesting as well as showing a
quickfinder for a variety of recent and upcoming events.
Previously, the start page redirected to the event history.
</li>
</ul>
<h5 class="articleSubheadline">May 2015</h5>
<ul class="bulletList">
<li>Competitors can have a separate flag image, such as a club flag to be displayed instead of
the nationality flag usually displayed with the sail number. This enables national league events
to display the club flags consistently when all nation flags would be the same anyway.
</li>
<li>The wind estimation from the tracked courses over ground has been improved. It now takes into
account any polar data available for the boat class concerned. In particular, this helps to rule
out very unlikely gybing angles in downwind clusters that otherwise could have led to an incorrect
split of the downwind fleet into two clusters where actually all boats were on the same tack.
</li>
<li>Some significant improvements have been applied to the simulator feature which is embedded in the
race viewer since some time now. The simulator now copes with very shifty wind conditions and finds
solutions in scenarios where an upwind leg flips to a reach or even into a downwind leg over time.
</li>
</ul>
<h5 class="articleSubheadline">April 2015</h5>
<ul class="bulletList">
<li>There is a new map setting "Wind-Up (show wind from top of map)". Selecting it will
align the map's top with the combined true wind average direction around the time currently
selected in the time slider. In this new view there are limitations as to how far you can zoom
out. Furthermore, you will not be able to use the Satellite view with this setting. Tip:
if you want to re-align to wind-up after a major wind shift, un-check the setting, confirm
the setting change, then set it again. A compass shows the true north orientation. Clicking
the compass will toggle between wind-up and north-up display.
</li>
<li>Bug fixes around addition of the "Regatta Rank" column in the leaderboard which could have
distorted the leaderboard's column layout under certain conditions.
</li>
<li>Bug fix for cumulative points in the leaderboard ("Show added scores for each entry in leaderboard").
With this setting, scores of competitors not tracked in the current race were not correctly represented
and may have stayed blank.
</li>
<li>Bug fix for the leg counter in the regatta page: when the first boat has finished, the leg counter went
to zero (e.g., "0 of 8"). This has now been fixed, and it will show, e.g., "8 of 8" upon the first boat
crossing the finish line.
</li>
</ul>
<h5 class="articleSubheadline">March 2015</h5>
<ul class="bulletList">
<li>Added competitor chart type for Course Over Ground (COG)
</li>
<li>Added leaderboard column and competitor chart entry for distances considering
gate starts. With gate starts, late starters sail a shorter distance in their first
leg. To still make distances comparable, a new metric has been introduced that for a gate
start adds the distance between the port side of the start line and the position where
the competitor started to the distance sailed in the first leg.
</li>
<li>The weights of the wind measurement units, the wind estimation and manual wind inputs by the
race committee are now used properly for the wind streamlet visualization. This fixes issues
with exaggerated weights for an estimated wind direction of very low confidence.
</li>
<li>The expand/collapse toggle button in the race viewer's time panel has correct arrows now.
</li>
</ul>
<h5 class="articleSubheadline">February 2015</h5>
<ul class="bulletList">
<li>The leaderboard offers a new column: "Start delay." It shows the number of seconds that passed
since the race start and the time the competitor passes the start mark / start line. As usual,
you can sort for this column. For line starts, this is a fairly direct measure of start quality
except for port starts which may have to be considered differently. For gate starts, you can use
this measure to find out about early and late starters and dig into possible correlations between
results and start time point.
</li>
</ul>
<h5 class="articleSubheadline">January 2015</h5>
<ul class="bulletList">
<li>The race viewer ("RaceBoard.html") now offers a new option in the map settings:
"Show simulation overlay". Checking this feature will simulate the current leader's leg
using different strategies and using the wind as it has been measured and estimated.
This feature becomes particularly interesting when combined with the "Show wind streamlets overlay"
feature that visualizes the spatial distribution of the wind field over the course.
</li>
<li>The race viewer is now more resilient against exceptional conditions such as the missing
of all wind information and the missing of a course with its waypoints. If no wind information
is available at all (no wind direction indicator is then shown in the upper left corner of
the map) then all ranking and distances will be based on a projection onto the course instead
of onto the wind direction, even for upwind and downwind legs.
</li>
</ul>
<h5 class="articleSubheadline">September 2014</h5>
<ul class="bulletList">
<li>The search feature now also includes results from currently ongoing events that may not yet be
included in our archive. Search keywords are now properly used in constructing the query.
</li>
</ul>
<h5 class="articleSubheadline">August 2014</h5>
<ul class="bulletList">
<li>The 2D race viewer, also known as the "Race Board," now comes with a fresh, new design. The orange
checkbox toggle buttons that open or close views such as the leaderboard or the charts for wind
and competitor comparison, have been replaced by tabs with "&gt;&gt;" symbols. Clicking on those
will expand or collapse the respective view. The views can now be resized using the handles attached
to the dividers separating the views. They work much better on mobile and touch devices than the splitters
used previously did. The settings buttons that used to be integrated with the orange toggle
checkbox buttons have now been moved into the views that they control. For example, the map settings
button is now displayed on the map, below the zoom control on the right map border.
</li>
<li>The leaderboard now has an improved search and filter control. Above the table displaying the
competitors and their figures is a new field with a text input that you can use to filter for
competitors, by their sail number or their name. While filtered, use the checkboxes in front of
the table row to add the competitor to or remove it from the selection. Use the "X" button to the
right of the search input field to clear the search. To the left of the search input field is a
filter symbol that you can use to select and create advanced filtering rules, such as filtering
by rank or nationality. Left of the filter button is the leaderboard settings button.
</li>
<li>The leaderboard has a new first column: a selection check box. We hope this makes it easier in
particular for users on mobile or other touch devices to select more than one competitor at a time.
The old-style multi-selection using the Control and Shift keys, as known well from typical desktop
file explorers, still work like they did before. Note that it is possible to sort for the checkbox
column, putting the selected competitors at the top of the leaderboard.
</li>
<li>When using the auto-zoom feature from the map settings, boats and wind streamlets will now
continue to move smoothly as long as the map only needs panning. Please also note that auto-zooming
to "tails" may slow down the display considerable, particularly when tracking at high frequency.
</li>
<li>When viewing a single race, the leaderboard now by default does no longer show the regatta rank as
the first column (and not as the second one either, given that the first column now usually shows
the checkboxes used for selecting items). If you'd like to get the regatta rank column back, also when
looking at a single race, please choose it from the leaderboard settings dialog where you can find it
in the "Overall Details" section. It may still be a nice column to have when following the last race
in a regatta. If you're just interested in the relative regatta rank ordering you may as well sort by
the right-most total points column which (surprising to some but welcome by most) sorts by the regatta
rank and not by the plain points sum (taking into account medal race and gold/silver/... fleet participations).
</li>
</ul>
<h5 class="articleSubheadline">July 2014</h5>
<ul class="bulletList">
<li>The advantage line now uses the wind at the position in the middle of the leg that the
competitor to which the line is attached is currently sailing in. Before, the "global" wind
average was used which is still displayed in the top left corner of the map. You can understand
the details by turning on the "Streamlet" wind visualization in the Map Settings.
</li>
<li>Faster wind streamlet loading: when you activate the streamlets for wind visualization, they now
display much faster.
</li>
<li>Faster boat position display: We found that what you're probably the most interested in when
you first open a race map or move the time slider to some very different point in time is the
position of the boats at that point in time; and you want to know it fast.
The tails which show how the boats got there are also interesting, but you won't blame us as much
if it takes a little bit to update them. So we've separated the placement of the boats from the
drawing of their tails. Now you may blame us for the tails temporarily not being attached to the
boats anymore ;-).
</li>
</ul>
<h5 class="articleSubheadline">June 2014</h5>
<ul class="bulletList">
<li>In the "Maps" settings in the RaceBoard page there is a new checkbox named
"Show wind streamlets overlay." If you select it and wait for about a minute,
a complete wind field will be calculated and visualized as an overlay on top
of the race course display. It reacts to you moving the time slider and shows
how the wind differs at different locations on the course. Stronger and faster-moving
streamlets mean stronger breeze.
</li>
<li>Wind is now calculated location-based. At locations closer to a sensor, that sensor
has a higher influence on the local wind field than sensors further away. The wind
estimations based on the courses sailed are assigned a location that corresponds with
the "center of gravity" of the field that was used for the estimation. For example,
if the fleet is approaching the windward mark and the wind sensor in on the committee
boat on the leeward side of the course, the course-based estimation influences the
assumed wind field more on the windward side whereas the sensor counts more for
the leeward side.
</li>
<li>Switched to the latest version of the Highcharts charting framework. This leads to improvements
especially on mobile devices. Charts can be zoomed using pinch gestures now.
</li>
<li>To support the use of the SAP Sailing Analytics also for live video streaming formats, we
introduced a new URL parameter <tt>viewShowMapControls</tt> for the <tt>Spectator.html</tt>
and the <tt>RaceBoard.html</tt> pages. Setting this parameter to "false"
will suppress the zoom, pan and map type selection
controls on the map display. Example:
<a href="http://danishleague2014.sapsailing.com/gwt/Spectator.html?leaderboardGroupName=Danish%20League%202014&showRaceDetails=true&viewShowMapControls=false">http://danishleague2014.sapsailing.com/gwt/Spectator.html?leaderboardGroupName=Danish%20League%202014&showRaceDetails=true&viewShowMapControls=false</a>.
When used for the <tt>Spectator.html</tt> URL, this will apply
to all <tt>RaceBoard.html</tt> pages (all races) reached from there. Of course, the map
can still be panned and zoomed using the usual mouse gestures. A left double click zooms
in, a right double click zooms out. Zoom can also be controlled using the mouse wheel.
And panning works by clicking on the map and dragging the mouse. Without the map type
control, switching to satellite view is not possible (keep this in mind when tracking
races in China, see below for Google's random coordinate transformations there).
</li>
</ul>
<h5 class="articleSubheadline">May 2014</h5>
<ul class="bulletList">
<li>Fixed an issue with the wind speed charts. Charts that only had a single, unchanged
wind speed crashed the browser. The solution scales the right hand side Y-axis denoting
the wind speed such that it always starts at 0kts.
</li>
<li>Analysis of tacks and jibes around mark passings has now been improved. Before,
a jibe followed by a tack that turns in the same direction (e.g., to port) so far
was always called a penalty maneuver. Now, if a mark passing separates such a tack
from such a jibe, no penalty is called anymore, and the two maneuvers are identified
correctly.
</li>
<li>Another note on maneuver analysis: we received questions regarding the maneuvers
involved in clearing an OCS. The SAP Sailing Analytics treat a clearing of
an OCS, going back behind the start line and crossing it again, as if the competitor
started again. In particular this means that all maneuvers prior to this new start
do not count for the metrics; neither does the distance sailed between the first (OCS)
start and the clear start.
</li>
<li>A hint regarding the "distance to start line" metrics: please keep in mind that currently this
measurement is calculated as the distance of the GPS tracker from the line. Unless the tracker
is mounted near the bow (or at the bow sprit, if any), the distance calculated this way will
be greater than what counts for crossing the line. Ordering, comparing and aggregating over this
figure still makes sense as long as you remember this detail.
</li>
<li>Introduced a new leaderboard entry "XTE +/-" featuring a signed cross-track error which shows
for a leg and also aggregated for an entire race on which side of the leg a competitor stayed on
average. Currently, the "side" is defined as viewing in the leg's direction, so "left" for a downwind
leg is the same side as "right" for the corresponding upwind leg.
</li>
</ul>
<h5 class="articleSubheadline">April 2014</h5>
<ul class="bulletList">
<li>China races, such as in Qingdao, may have the map background displayed at slightly wrong locations.
This is due to an intentional random transformation that Google applies to GPS locations for
China. You can circumvent this issue by switching to satellite view where the map background is displayed
correctly. <img src="/images/mapsatellite.png"/>
</li>
<li>Fixed a bug that in some cases has caused the wind chart, the advantage line, the wind indicator on the map
and the leaderboard to use slightly different wind values. The problem was related to different averaging and
damping methods used for these values which has now been aligned.
</li>
<li>Fixed a bug that caused the time spent in a leg, race or regatta to always show the time at the end of the leg
if the leg has been started by the competitor at the time point queried, e.g., by the time slider. While this
did not have any serious effects in live mode, for replay races this left the leg time and race time constant
at the total time spent so far up to the end of the last leg the competitor had started at that time.
</li>
</ul>
<h5 class="articleSubheadline">March 2014</h5>
<ul class="bulletList">
<li>Added a new setting for a leaderboard called "Show added scores". Using this feature one
can display added scores for each leaderboard entry. An added score is the sum over all scores
from the first column to the current column. One can describe it as an intermediate overall score.
This feature can be used to know the score until a specific race while another race is live and
constantly changes the overall score.</li>
<li>Added tooltips for leaderboard settings.</li>
<li>Fixed HighPointOverall scheme to also break a tie by looking at the ranks of the last regatta.</li>
<li>Progress indicator when expanding a race in the RaceBoard view</li>
<li>Leaderboard column "GPS Lag" shows how far a competitor's GPS signal lags behind the average
tracking quality. A background color bar classifies into good/ok/bad (green/yellow/red).</li>
</ul>
<h5 class="articleSubheadline">February 2014</h5>
<ul class="bulletList">
<li>Fixed an issue with boat rotation on Safari browsers</li>
<li>Maneuver analysis now counts tacks and jibes based on wind
at boat position and not based on leg type; subsequently,
penalty circle analysis is now based on a tack and a jibe
in close succession instead of a full circle.</li>
<li>Regatta rank and overall rank in competitor chart of leaderboard panel
has been fixed; it did erroneously display the rank of the particular
race, not the regatta/overall rank after that race.</li>
<li>Regatta overview now also displays all regatta leaderboards</li>
<li>Boat colors match tail colors</li>
<li>When selecting boats in leaderboard, de-selected boats' tails turn grey</li>
</ul>
<h5 class="articleSubheadline">January 2014</h5>
<ul class="bulletList">
<li>Boat movement animated smoothly</li>
<li>Start and Finish line show line parameters (length, angle to wind,
advantage) when mouse hovers over them</li>
<li>Start line has a count-down indicator displayed next to it
before the start. The count-down is also displayed underneath the time
slider</li>
<li>Competitor and Wind charts show pre-start data where possible</li>
<li>Competitors can be assigned a fixed color which they will keep
for all races in a regatta</li>
<li>By default, videos are now shown in a moveable, resizable box embedded in
the web page. For a two screen setup, the embedded box can be popped out to a
dedicated browser window.</li>
<li>Official score corrections for historic races are only applied after
the respective competitor finished the race. Therefore, in-between race
scores now correspond with tracking, regardless of final corrected score.</li>
<li>By using negative playback speed (e.g., -1), replays can be run backwards.</li>
</ul>
</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -9,7 +9,7 @@ import com.sap.sailing.selenium.pages.PageArea;
import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaListCompositePO.RegattaDescriptor;
public class RegattaStructureManagementPanelPO extends PageArea {
public static final String DEFAULT_SERIES_NAME = "Default"; //$NON-NLS-1$
public static final String DEFAULT_SERIES_NAME = "DefaultSelenium"; //$NON-NLS-1$
@FindBy(how = BySeleniumId.class, using = "AddRegattaButton")
WebElement addRegattaButton;
@@ -57,7 +57,7 @@ public class TestSmartphoneTrackingEventManagementPanel extends AbstractSelenium
String raceName = String.format(RACE, i);
TrackableRaceDescriptor trackableRace = new TrackableRaceDescriptor(EVENT, raceName, BOAT_CLASS);
TrackedRaceDescriptor trackedRace = new TrackedRaceDescriptor(this.regatta.toString(), BOAT_CLASS, raceName);
RaceDescriptor leaderboardRace = new RaceDescriptor(String.format("R%s", i), "Default", false, false, 0);
RaceDescriptor leaderboardRace = new RaceDescriptor(String.format("D%s", i), "Default", false, false, 0);
this.trackableRaces.add(trackableRace);
this.trackedRaces.add(trackedRace);
@@ -50,7 +50,7 @@ public class TestColumnsInLeaderboard extends AbstractSeleniumTest {
this.regatta = new RegattaDescriptor(REGATTA, BOAT_CLASS);
this.trackableRace = new TrackableRaceDescriptor(EVENT, RACE, BOAT_CLASS);
this.trackedRace = new TrackedRaceDescriptor(this.regatta.toString(), BOAT_CLASS, RACE);
this.raceColumn = new RaceDescriptor("R1", "Default", false, false, 0.0);
this.raceColumn = new RaceDescriptor("D1", "Default", false, false, 0.0);
clearState(getContextRoot());
super.setUp();
configureLeaderboard();
@@ -60,7 +60,7 @@ public class TestLeaderboardConfiguration extends AbstractSeleniumTest {
String raceName = String.format(RACE, i);
TrackableRaceDescriptor trackableRace = new TrackableRaceDescriptor(EVENT, raceName, BOAT_CLASS);
TrackedRaceDescriptor trackedRace = new TrackedRaceDescriptor(this.regatta.toString(), BOAT_CLASS, raceName);
RaceDescriptor leaderboardRace = new RaceDescriptor(String.format("R%s", i), "Default", false, false, 0);
RaceDescriptor leaderboardRace = new RaceDescriptor(String.format("D%s", i), "Default", false, false, 0);
this.trackableRaces.add(trackableRace);
this.trackedRaces.add(trackedRace);
@@ -100,12 +100,12 @@ public class TestLeaderboardConfiguration extends AbstractSeleniumTest {
leaderboard.refresh();
List<LeaderboardEntry> allEntries = table.getEntries();
LeaderboardEntry findelJens = table.getEntry("8875");
Integer points = findelJens.getPointsForRace(String.format("R%s", i + 1));
Integer points = findelJens.getPointsForRace(String.format("D%s", i + 1));
Integer rank = findelJens.getTotalRank();
// Assertions
assertThat("Number of competitors does not match",
allEntries.size(), equalTo(28));
assertThat("Points for race 'R" + (i + 1) + "' do not match for competitor '8875' (Findel, Jens)",
assertThat("Points for race 'D" + (i + 1) + "' do not match for competitor '8875' (Findel, Jens)",
points, equalTo(expectedPointsForFindelJens[i]));
assertThat("Total rank after " + (i + 1) + " race(s) does not match for competitor '8875' (Findel, Jens)",
rank, equalTo(expectedRankForFindelJens[i]));
@@ -136,13 +136,13 @@ public class TestLeaderboardConfiguration extends AbstractSeleniumTest {
RegattaStructureManagementPanelPO regattaStructure = adminConsole.goToRegattaStructure();
RegattaDetailsCompositePO regattaDetails = regattaStructure.getRegattaDetails(this.regatta);
SeriesEditDialogPO seriesDialog = regattaDetails.editSeries(RegattaStructureManagementPanelPO.DEFAULT_SERIES_NAME);
seriesDialog.deleteRace("R3");
seriesDialog.deleteRace("D3");
seriesDialog.pressOk(true);
// Now we can check the result with our expectation
leaderboardWindow.switchToWindow();
leaderboard.refresh();
assertThat("Race names do not match after deletion of race 'R3'",
table.getRaceNames(), equalTo(Arrays.asList("R1", "R2", "R4", "R5")));
assertThat("Race names do not match after deletion of race 'D3'",
table.getRaceNames(), equalTo(Arrays.asList("D1", "D2", "D4", "D5")));
}
@Ignore("This test belongs to bug 1892 and currently fails. It is currently enabled on branch bug1892.")
@@ -175,15 +175,15 @@ public class TestLeaderboardConfiguration extends AbstractSeleniumTest {
RegattaStructureManagementPanelPO regattaStructure = adminConsole.goToRegattaStructure();
RegattaDetailsCompositePO regattaDetails = regattaStructure.getRegattaDetails(this.regatta);
SeriesEditDialogPO seriesDialog = regattaDetails.editSeries(RegattaStructureManagementPanelPO.DEFAULT_SERIES_NAME);
seriesDialog.renameRace("R1", "Q");
seriesDialog.renameRace("D1", "Q");
seriesDialog.pressOk(true);
// Now we can check the result with our expectation
leaderboardWindow.switchToWindow();
leaderboard.refresh();
List<String> races = table.getRaceNames();
assertThat("Race names do not match after renaming race 'R1' to 'Q'",
races, equalTo(Arrays.asList("Q", "R2", "R3", "R4", "R5")));
assertThat("Race names do not match after renaming race 'D1' to 'Q'",
races, equalTo(Arrays.asList("Q", "D2", "D3", "D4", "D5")));
}
private void configureLeaderboard() {
@@ -54,7 +54,7 @@ public class TestSwitchingOffStartOfRaceInference extends AbstractSeleniumTest {
this.regatta = new RegattaDescriptor(REGATTA, BOAT_CLASS);
this.trackableRace = new TrackableRaceDescriptor(EVENT, RACE, BOAT_CLASS);
this.trackedRace = new TrackedRaceDescriptor(this.regatta.toString(), BOAT_CLASS, RACE);
this.raceColumn = new RaceDescriptor("R3", "Default", false, false, 0.0);
this.raceColumn = new RaceDescriptor("D3", "Default", false, false, 0.0);
clearState(getContextRoot());
super.setUp();
adminConsole = configureRegattaAndLeaderboard();
@@ -69,10 +69,10 @@ public class TestSwitchingOffStartOfRaceInference extends AbstractSeleniumTest {
LeaderboardPage leaderboard = LeaderboardPage.goToPage(getWebDriver(), getContextRoot(), LEADERBOARD, /* race details */ false);
LeaderboardTablePO leaderboardTable = leaderboard.getLeaderboardTable();
List<String> races = leaderboardTable.getRaceNames();
assertThat("Expected only R3", races, equalTo(Arrays.asList("R3")));
int r3ColumnIndex = leaderboardTable.getColumnIndex("R3");
assertThat("Expected only D3", races, equalTo(Arrays.asList("D3")));
int d3ColumnIndex = leaderboardTable.getColumnIndex("D3");
for (LeaderboardEntry e : leaderboardTable.getEntries()) {
String raceColumnContent = e.getColumnContent(r3ColumnIndex);
String raceColumnContent = e.getColumnContent(d3ColumnIndex);
assertTrue(Integer.parseInt(raceColumnContent) > 0); // all competitors have a positive score in R3
}
adminConsoleWindow.switchToWindow();
@@ -85,7 +85,7 @@ public class TestSwitchingOffStartOfRaceInference extends AbstractSeleniumTest {
leaderboardWindow.switchToWindow();
leaderboard.refresh();
for (LeaderboardEntry e : leaderboardTable.getEntries()) {
String raceColumnContent = e.getColumnContent(r3ColumnIndex);
String raceColumnContent = e.getColumnContent(d3ColumnIndex);
assertEquals("", raceColumnContent); // all competitors have an empty score because there is no start time anymore
}
leaderboardWindow.close();
@@ -27,10 +27,19 @@
<div class="innerContent">
<h2 class="articleSubheadline">February 2016</h2>
<ul class="bulletList">
<li>Several improvements regarding the creation of race metadata (events, regatta, etc.) including:
automatic creation of default series, dialog for creating a default leaderboard group
</li>
<li>The RaceBoard URL Parameter "canReplayDuringLiveRaces" has been removed.
This parameter was used to allow users to trigger autoplay for live races on non-live points in time.
The permission "CAN_REPLAY_DURING_LIVE_RACES" and role "moderator" now grant this functionality to users.
</li>
<li>The "Manage Media" button in RegattaOverview is now bound to the Permission "MANAGE_MEDIA".
The Roles "eventmanager" and "mediaeditor" grant the "MANAGE_MEDIA" permission.
</li>
<li>The "Edit mark passings" button in RegattaOverview is now bound to the Permission "MANAGE_MARK_PASSINGS".
The Role "eventmanager" grants the "MANAGE_MARK_PASSINGS" permission.
</li>
<li>The user management page now provides a "Refresh" button to reload the user list.
</li>
<li>The user management page now also lists sailing roles and permissions in the respective suggest boxes.
@@ -37,7 +37,7 @@ public class AmazonS3FileStorageServiceImpl extends BaseFileStorageServiceImpl i
private static final Logger logger = Logger.getLogger(AmazonS3FileStorageServiceImpl.class.getName());
private static final String retrievalProtocol = "http";
private static final String retrievalProtocol = "https";
private static final String regionRetrievalHost = "s3-eu-west-1.amazonaws.com";
private final FileStorageServicePropertyImpl accessId = new FileStorageServicePropertyImpl("accessId", false,
@@ -330,7 +330,7 @@ body ::-webkit-scrollbar-track {
}
.gwt-MapSettingsButton {
margin-right: 30px;
margin-right: 10px;
margin-top: 5px;
border-top-left-radius: 25px;
border-top-right-radius: 25px;
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: TracTrac Client module
Bundle-SymbolicName: com.tractrac.clientmodule
Bundle-Version: 3.1.3
Bundle-Version: 3.1.5
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ClassPath: .,
lib/TracAPI.jar
+26 -1
View File
@@ -15,6 +15,31 @@ It contains also some files:
- test.sh -> script that compiles the code in the src folder, creates the test.jar library and execute the code of the example.
- Manifest.txt -> manifest used to create the test.jar file
********************************************
************* TracAPI 3.1.5 ****************
********************************************
This is a final version. Only fixes bugs in the implementation
Release date: 17/02/2016
Build number: 11948
1) Bugs
- The bug of the previous release (3.1.2) was not fixed.
********************************************
************* TracAPI 3.1.4 ****************
********************************************
This is a final version. Only fixes bugs in the implementation
Release date: 15/02/2016
Build number: 11938
1) Bugs
- An infinity loop has been detected in the thread that sends the application
messages (Repored by Axel Uhl, 12/02/2016)
********************************************
************* TracAPI 3.1.3 ****************
@@ -26,7 +51,7 @@ Build number: 11888
1) Bugs
- The bug of the previous release (3.1.3) was not fixed.
- The bug of the previous release (3.1.2) was not fixed.
********************************************
************* TracAPI 3.1.2 ****************
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -8,6 +8,6 @@
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.tractrac.clientmodule</artifactId>
<version>3.1.3</version>
<version>3.1.5</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -3,7 +3,6 @@ package com.sap.sailing.android.tracking.app.ui.fragments;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
@@ -16,9 +15,9 @@ import android.widget.TextView;
import com.sap.sailing.android.shared.logging.ExLog;
import com.sap.sailing.android.shared.services.sending.MessageSendingService.APIConnectivity;
import com.sap.sailing.android.shared.ui.customviews.SignalQualityIndicatorView;
import com.sap.sailing.android.tracking.app.BuildConfig;
import com.sap.sailing.android.tracking.app.R;
import com.sap.sailing.android.shared.ui.customviews.SignalQualityIndicatorView;
import com.sap.sailing.android.tracking.app.services.TrackingService.GPSQuality;
import com.sap.sailing.android.tracking.app.ui.activities.TrackingActivity;
import com.sap.sailing.android.tracking.app.utils.AppPreferences;
@@ -384,13 +384,13 @@ public class CirclePageIndicator extends View implements PageIndicator {
return;
}
if (mViewPager != null) {
mViewPager.setOnPageChangeListener(null);
mViewPager.clearOnPageChangeListeners();
}
if (view.getAdapter() == null) {
throw new IllegalStateException("ViewPager does not have adapter instance.");
}
mViewPager = view;
mViewPager.setOnPageChangeListener(this);
mViewPager.addOnPageChangeListener(this);
invalidate();
}
@@ -115,14 +115,14 @@ public class IconPageIndicator extends HorizontalScrollView implements PageIndic
return;
}
if (mViewPager != null) {
mViewPager.setOnPageChangeListener(null);
mViewPager.clearOnPageChangeListeners();
}
PagerAdapter adapter = view.getAdapter();
if (adapter == null) {
throw new IllegalStateException("ViewPager does not have adapter instance.");
}
mViewPager = view;
view.setOnPageChangeListener(this);
view.addOnPageChangeListener(this);
notifyDataSetChanged();
}
@@ -296,13 +296,13 @@ public class LinePageIndicator extends View implements PageIndicator {
}
if (mViewPager != null) {
// Clear us from the old pager.
mViewPager.setOnPageChangeListener(null);
mViewPager.clearOnPageChangeListeners();
}
if (viewPager.getAdapter() == null) {
throw new IllegalStateException("ViewPager does not have adapter instance.");
}
mViewPager = viewPager;
mViewPager.setOnPageChangeListener(this);
mViewPager.addOnPageChangeListener(this);
invalidate();
}
@@ -196,14 +196,14 @@ public class TabPageIndicator extends HorizontalScrollView implements PageIndica
return;
}
if (mViewPager != null) {
mViewPager.setOnPageChangeListener(null);
mViewPager.clearOnPageChangeListeners();
}
final PagerAdapter adapter = view.getAdapter();
if (adapter == null) {
throw new IllegalStateException("ViewPager does not have adapter instance.");
}
mViewPager = view;
view.setOnPageChangeListener(this);
view.addOnPageChangeListener(this);
notifyDataSetChanged();
}
@@ -737,13 +737,13 @@ public class TitlePageIndicator extends View implements PageIndicator {
return;
}
if (mViewPager != null) {
mViewPager.setOnPageChangeListener(null);
mViewPager.clearOnPageChangeListeners();
}
if (view.getAdapter() == null) {
throw new IllegalStateException("ViewPager does not have adapter instance.");
}
mViewPager = view;
mViewPager.setOnPageChangeListener(this);
mViewPager.addOnPageChangeListener(this);
invalidate();
}
@@ -288,13 +288,13 @@ public class UnderlinePageIndicator extends View implements PageIndicator {
}
if (mViewPager != null) {
// Clear us from the old pager.
mViewPager.setOnPageChangeListener(null);
mViewPager.clearOnPageChangeListeners();
}
if (viewPager.getAdapter() == null) {
throw new IllegalStateException("ViewPager does not have adapter instance.");
}
mViewPager = viewPager;
mViewPager.setOnPageChangeListener(this);
mViewPager.addOnPageChangeListener(this);
invalidate();
post(new Runnable() {
@Override
@@ -9,7 +9,6 @@ import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.AttrRes;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.util.TypedValue;
import android.view.View;
+1 -1
View File
@@ -28,4 +28,4 @@ gen-external-apklibs/
# Other #
#########
.~lock*
tmp
tmp
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="src" path="src"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>de.psdev.licensesdialog</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
@@ -0,0 +1,4 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.source=1.6
@@ -11,5 +11,6 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-14
target=android-22
android.library=true
android.library.reference.1=../../google-android-support-v7-appcompat_lib
+1 -1
View File
@@ -26,7 +26,7 @@
<!-- redefine in child poms -->
<additional-deployment-version-file></additional-deployment-version-file>
<app-version></app-version>
<android.support.version>22.1.1</android.support.version>
<android.support.version>22.2.1</android.support.version>
<play.services.version>7.5.0</play.services.version>
<sign.keystore>../android-keystore/STG-debug.jks</sign.keystore>
+49
View File
@@ -0,0 +1,49 @@
# Supporting HTTPS / SSL
On sapsailing.com, we have a security certificate installed. For root users its key is visible at `/etc/pki/tls/private/star_sapsailing_com.key`. The certicicate is at `/etc/pki/tls/certs/star_sapsailing_com.crt`. To enable SSL on a server, the module `mod_ssl` needs to be installed. Use ``yum install mod_ssl``.
The `/etc/httpd/conf/httpd.conf` file must contain the following in order to ensure that the `ServerName` setting is respected for selecting the appropriate `VirtualHost` element:
<pre>
NameVirtualHost *:80
NameVirtualHost *:443
</pre>
When this is done, the server can use the following sequence of directives inside a &lt;VirtualHost&gt; element:
<pre>
SSLEngine On
SSLCertificateFile /etc/pki/tls/certs/star_sapsailing_com.crt
SSLCertificateKeyFile /etc/pki/tls/private/star_sapsailing_com.key
</pre>
A full-blown &lt;VirtualHost&gt; element in a rewrite macro in `000-macros.conf` then could look like this:
```
<Macro Event-SSL $HOSTNAME $EVENTUUID $IP $PORT>
<VirtualHost *:443>
ServerName $HOSTNAME
SSLEngine On
SSLCertificateFile /etc/pki/tls/certs/star_sapsailing_com.crt
SSLCertificateKeyFile /etc/pki/tls/private/star_sapsailing_com.key
Use Headers
RewriteEngine on
RewriteCond %{REQUEST_URI} "^(/)?$"
RewriteRule ^(/)?$ "https://$HOSTNAME/gwt/Home.html?%{QUERY_STRING}#/event/:eventId=$EVENTUUID" [L,NE]
Use Rewrite $IP $PORT
</VirtualHost>
</Macro>
```
It can then be used inside the `001-events.conf` file like this
```
Use Event-SSL ssltest.sapsailing.com "f8087b3c-c641-4fda-bf8d-0bc2abe09e40" 172.31.22.239 8888
```
Keep in mind that the certificate we have only is valid for `*.sapsailing.com` which does not include 2nd-level sub-domains such as a.b.sapsailing.com.