Merge branch 'gwt' into up_down

Conflicts:
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/LeaderboardPanel.java
This commit is contained in:
Axel Uhl committed 2011-11-02 17:09:52 +01:00
commit 1bbb316b65
22 files changed
+181 -35

No files matched your search

+32
View File
@@ -1,6 +1,34 @@
Axel
====
- migrate to Virgo; clarify use of Maven (snapshot builds vs. version builds) and the effects this has
on the runtime's selection of bundle versions
- store tracking data in our own DB; enables historical viewing as well as cross-regatta analysis
- install Bugzilla on kielweek.web4sap.com and move this file's contents to it
- add visualizations to leaderboard, such as rank/distance comparison between selected set of competitors
- stripped-down race selection and live leaderboard that works well on smartphones; show marks as they
pass through the fleet
- visualize results of Douglas-Peucker algorithm in Google Map
- implement maneuver analysis, particularly counting tacks / jibes and computing maneuver loss
- implement start advantage analysis including respective leaderboard visualization
- implement risk assessment: 1) risk of getting too close to layline in top sector; 2) risk of getting
passed by successors due to wind shifts; in all this consider wind patterns (how shifty / gusty is the wind?)
- improve performance of Google Map
- understand how to add layers on top of Google Map and use this to visualize, e.g., wind field or risk
- improve look of boats in Google Map; consider using canvas, draw to a canvas and use the result as marker image
in map in order to display boat direction properly
- for WithID objects use their ID, particularly in Mongo
- Test adding waypoints to an existing course using TracTrac server and see if events are transmitted.
@@ -90,6 +118,10 @@ Axel
- Implement refined version of isValid in GPSFixMovingTrack taking into account the
speed as contained by each individual fix
- SwissTiming connector
- ManyPlayers collaboration: widgets, UI integration, GPS and wind data integration
- Refactor the way the DomainFactory concept works, under the assumption that we'll have another
connector for SwissTiming and VirtualEye. Our domain model including the Tracked... stuff should remain in place.
Therefore, the link between the domain objects RaceDefinition, Leg, Course and Event with their
@@ -1,6 +1,5 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.FileNotFoundException;
@@ -64,7 +63,6 @@ public abstract class AbstractTracTracLiveTest extends StoredTrackBasedTest impl
// Read event data from configuration file
event = KeyValue.setup(paramUrl);
assertNotNull(event);
assertEquals(getExpectedEventName(), event.getName());
// Initialize data controller using live and stored data sources
controller = new DataController(liveUri, storedUri, this);
// Start live and stored data streams
@@ -50,8 +50,8 @@ public class FetchTracksAndStoreLocallyTest extends KielWeek2011BasedTest {
* into {@link #firstTracked} and {@link #firstData}. All events are converted into {@link GPSFixMovingImpl}
* objects and appended to the {@link DynamicTrackedRace}s.
*/
private void setUp(String raceId) throws MalformedURLException, IOException, InterruptedException, URISyntaxException {
super.setUpWithoutLaunchingController(raceId);
private void setUp(String eventName, String raceId) throws MalformedURLException, IOException, InterruptedException, URISyntaxException {
super.setUpWithoutLaunchingController(eventName, raceId);
final RaceChangeListener<Competitor> positionListener = new RaceChangeListener<Competitor>() {
@Override
public void gpsFixReceived(GPSFix fix, Competitor competitor) {
@@ -150,21 +150,28 @@ public class FetchTracksAndStoreLocallyTest extends KielWeek2011BasedTest {
"bc78290c-9f55-11e0-85be-406186cbf87c", "d62d4288-9f55-11e0-85be-406186cbf87c",
"972c4d74-9f53-11e0-85be-406186cbf87c", "c3ab8bcc-a17d-11e0-aeec-406186cbf87c" }) {
System.out.println("Loading and storing race "+raceId);
storeRace(raceId);
storeRace("event_20110609_KielerWoch", raceId);
}
}
@Ignore
public void store505Race2() throws InterruptedException, FileNotFoundException, IOException, URISyntaxException {
storeRace("357c700a-9d9a-11e0-85be-406186cbf87c");
storeRace("event_20110609_KielerWoch", "357c700a-9d9a-11e0-85be-406186cbf87c");
}
private void storeRace(String raceId) throws MalformedURLException, IOException, InterruptedException,
private void storeRace(String eventName, String raceId) throws MalformedURLException, IOException, InterruptedException,
URISyntaxException, FileNotFoundException {
setUp(raceId);
setUp(eventName, raceId);
storeTracks();
}
public static void main(String[] args) throws URISyntaxException, FileNotFoundException, IOException, InterruptedException {
FetchTracksAndStoreLocallyTest thiz = new FetchTracksAndStoreLocallyTest();
for (int i=1; i<args.length; i++) {
thiz.storeRace(args[0], args[i]);
}
}
private void storeTracks() throws FileNotFoundException, IOException {
for (Map.Entry<Competitor, DynamicTrack<Competitor, GPSFixMoving>> competitorAndTrack : tracks.entrySet()) {
Competitor competitor = competitorAndTrack.getKey();
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -37,8 +38,8 @@ import com.tractrac.clientmodule.Race;
/**
* Connects to the Kieler Woche 2011 TracTrac data of the 505 Race 2. Subclasses should implement a @Before method which
* calls {@link #setUp(String, ReceiverType[])} with a useful set of receiver types and the race they want to observe /
* load, or they should call {@link #setUp(String, ReceiverType[])} at the beginning of each respective test in case
* calls {@link #setUp(String, String, ReceiverType[])} with a useful set of receiver types and the race they want to observe /
* load, or they should call {@link #setUp(String, String, ReceiverType[])} at the beginning of each respective test in case
* they want to select/load different races for different tests. When all stored data has been received, the
* {@link #getSemaphor() semaphor} is notified. Therefore, a typical pattern for subclasses should be to invoke
* {@link #setUp(ReceiverType[])}, then wait on the semaphor before starting with test processing.
@@ -73,8 +74,10 @@ public abstract class KielWeek2011BasedTest extends AbstractTracTracLiveTest {
// to select a race
}
protected void setUp(String raceId, ReceiverType... receiverTypes) throws MalformedURLException, IOException, InterruptedException, URISyntaxException {
setUpWithoutLaunchingController(raceId);
protected void setUp(String eventName, String raceId, ReceiverType... receiverTypes) throws MalformedURLException,
IOException, InterruptedException, URISyntaxException {
setUpWithoutLaunchingController(eventName, raceId);
assertEquals(getExpectedEventName(), getEvent().getName());
completeSetupLaunchingControllerAndWaitForRaceDefinition(receiverTypes);
}
@@ -111,11 +114,14 @@ public abstract class KielWeek2011BasedTest extends AbstractTracTracLiveTest {
}
protected void setUpWithoutLaunchingController(String raceId) throws FileNotFoundException, MalformedURLException,
protected void setUpWithoutLaunchingController(String eventName, String raceId) throws FileNotFoundException, MalformedURLException,
URISyntaxException {
super.setUp(new URL("http://germanmaster.traclive.dk/events/event_20110609_KielerWoch/clientparams.php?event=event_20110609_KielerWoch&race="+raceId),
super.setUp(new URL("http://germanmaster.traclive.dk/events/"+eventName+"/clientparams.php?event="+eventName+"&race="+raceId),
tractracTunnel ? new URI("tcp://"+tractracTunnelHost+":4412") : new URI("tcp://germanmaster.traclive.dk:4400"),
tractracTunnel ? new URI("tcp://"+tractracTunnelHost+":4413") : new URI("tcp://germanmaster.traclive.dk:4401"));
if (domainFactory == null) {
domainFactory = new DomainFactoryImpl();
}
domainEvent = domainFactory.getOrCreateEvent(getEvent());
trackedEvent = domainFactory.getOrCreateTrackedEvent(domainEvent);
}
@@ -59,7 +59,7 @@ public class LeaderboardForKielWeekTest extends KielWeek2011BasedTest {
private void loadRace(String raceId) throws MalformedURLException, IOException, InterruptedException,
URISyntaxException {
setUp(raceId, ReceiverType.RACECOURSE, ReceiverType.RACESTARTFINISH, ReceiverType.MARKPASSINGS);
setUp("event_20110609_KielerWoch", raceId, ReceiverType.RACECOURSE, ReceiverType.RACESTARTFINISH, ReceiverType.MARKPASSINGS);
getTrackedRace().setWindSource(WindSource.WEB);
getTrackedRace().recordWind(new WindImpl(/* position */ null, MillisecondsTimePoint.now(),
new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(70))), WindSource.WEB);
@@ -36,8 +36,8 @@ public class ManeuverDetectionOnKielerWoche505Race2DataTest extends KielWeek2011
@Before
public void setUp() throws MalformedURLException, IOException, InterruptedException, URISyntaxException {
super.setUp();
super.setUp(/* raceId */ "357c700a-9d9a-11e0-85be-406186cbf87c",
new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.RACECOURSE, ReceiverType.RAWPOSITIONS });
super.setUp("event_20110609_KielerWoch",
/* raceId */ "357c700a-9d9a-11e0-85be-406186cbf87c", new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.RACECOURSE, ReceiverType.RAWPOSITIONS });
KielWeek2011BasedTest.fixApproximateMarkPositionsForWindReadOut(getTrackedRace());
getTrackedRace().setWindSource(WindSource.WEB);
getTrackedRace().recordWind(new WindImpl(/* position */ null, MillisecondsTimePoint.now(),
@@ -76,11 +76,15 @@ public class ParseTracTracJSONServiceOutputTest {
"http://germanmaster.traclive.dk/events/event_20110505_SailingTea/jsonservice.php"));
assertEquals("Sailing Team Germany", jsonService.getEventName());
List<RaceRecord> races = jsonService.getRaceRecords();
assertEquals(28, races.size());
assertTrue(races.size()>=28);
int found = 0;
for (RaceRecord race : races) {
assertNotNull(race.getReplayURL());
assertTrue(race.getName().startsWith("weym") || race.getName().startsWith("kiwo") || race.getName().startsWith("schwerttest"));
if (race.getName().startsWith("weym") || race.getName().startsWith("kiwo") || race.getName().startsWith("schwerttest")) {
found++;
}
}
assertTrue(found >= 28);
}
@Test
@@ -30,8 +30,8 @@ public class WindEstimationOnKielerWoche505Race2DataTest extends KielWeek2011Bas
@Before
public void setUp() throws MalformedURLException, IOException, InterruptedException, URISyntaxException {
super.setUp();
super.setUp(/* raceId */ "357c700a-9d9a-11e0-85be-406186cbf87c",
new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.RACECOURSE, ReceiverType.RAWPOSITIONS });
super.setUp("event_20110609_KielerWoch",
/* raceId */ "357c700a-9d9a-11e0-85be-406186cbf87c", new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.RACECOURSE, ReceiverType.RAWPOSITIONS });
KielWeek2011BasedTest.fixApproximateMarkPositionsForWindReadOut(getTrackedRace());
getTrackedRace().setWindSource(WindSource.WEB);
getTrackedRace().recordWind(new WindImpl(/* position */ null, MillisecondsTimePoint.now(),
@@ -389,4 +389,10 @@ public class MockedTrackedRace implements DynamicTrackedRace {
return null;
}
@Override
public Wind getDirectionFromStartToNextMark(TimePoint at) {
// TODO Auto-generated method stub
return null;
}
}
@@ -204,4 +204,6 @@ public interface TrackedRace {
Tack getTack(Competitor competitor, TimePoint timePoint);
TrackedEvent getTrackedEvent();
Wind getDirectionFromStartToNextMark(TimePoint at);
}
@@ -427,14 +427,21 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
" Future warnings of this type will be suppressed for this race.");
warnedOfUsingLegDirectionAsWindEstimation = true;
}
Leg firstLeg = getRace().getCourse().getLegs().iterator().next();
Position firstLegEnd = getApproximatePosition(firstLeg.getTo(), at);
Position firstEndStart = getApproximatePosition(firstLeg.getFrom(), at);
result = new WindImpl(p, at, new KnotSpeedWithBearingImpl(1.0, firstLegEnd.getBearingGreatCircle(firstEndStart)));
result = getDirectionFromStartToNextMark(at);
}
}
return result;
}
@Override
public Wind getDirectionFromStartToNextMark(TimePoint at) {
Wind result;
Leg firstLeg = getRace().getCourse().getLegs().iterator().next();
Position firstLegEnd = getApproximatePosition(firstLeg.getTo(), at);
Position firstLegStart = getApproximatePosition(firstLeg.getFrom(), at);
result = new WindImpl(firstLegStart, at, new KnotSpeedWithBearingImpl(1.0, firstLegEnd.getBearingGreatCircle(firstLegStart)));
return result;
}
@Override
public WindSource getWindSource() {
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.sap.sailing.gwt.ui.test</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
@@ -0,0 +1,8 @@
#Wed Nov 02 13:57:42 CET 2011
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.6
@@ -0,0 +1,4 @@
#Wed Nov 02 13:57:42 CET 2011
eclipse.preferences.version=1
pluginProject.extensions=false
resolve.requirebundle=false
+9
View File
@@ -0,0 +1,9 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Test
Bundle-SymbolicName: com.sap.sailing.gwt.ui.test
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Require-Bundle: com.sap.sailing.gwt.ui,
org.junit4;bundle-version="4.8.1"
+4
View File
@@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
@@ -27,4 +27,5 @@ Bundle-ClassPath: lib/gwt-maps.jar,
.
Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator
Bundle-ActivationPolicy: lazy
Export-Package: com.sap.sailing.gwt.ui.server
Export-Package: com.sap.sailing.gwt.ui.server,
com.sap.sailing.gwt.ui.shared;x-friends:="com.sap.sailing.gwt.ui.test"
@@ -800,15 +800,21 @@ public class LeaderboardPanel extends FormPanel implements TimeListener,
loadCompleteLeaderboard(getLeaderboardDisplayDate());
VerticalPanel vp = new VerticalPanel();
vp.setSpacing(15);
DockPanel logoAndSettings = new DockPanel();
vp.add(logoAndSettings);
HorizontalPanel logoAndTitle = new HorizontalPanel();
vp.add(logoAndTitle);
Anchor sapLogo = new Anchor(
new SafeHtmlBuilder()
.appendHtmlConstant(
"<img class=\"linkNoBorder\" src=\"/images/sap_66_transparent.png\"/>")
.toSafeHtml());
sapLogo.setHref("http://www.sap.com");
vp.add(sapLogo);
logoAndTitle.add(sapLogo);
Label sailingAnalyticsLabel = new Label(stringConstants.sapSailingAnalytics());
HorizontalPanel labelPanel = new HorizontalPanel();
labelPanel.add(sailingAnalyticsLabel);
labelPanel.setSpacing(10);
logoAndTitle.add(labelPanel);
sailingAnalyticsLabel.addStyleName("boldLabel");
DockPanel dockPanel = new DockPanel();
dockPanel.setWidth("100%");
dockPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
@@ -60,7 +60,7 @@ leg=L
distanceInMeters=Distance/m
averageSpeedInKnots=Average Speed/kts
rankGain=Rank Gain
sapSailingAnalytics=SAP Sailing Analytics
sapSailingAnalytics=Sailing Analytics
leaderboard=LEADERBOARD
leaderboardSettings=Leaderboard Settings
selectLegDetails=Select Timing and Leg Details
@@ -101,4 +101,4 @@ timing=Timing
raceDetailsToShow=Race Details
legDetailsToShow=Leg Details
columnMoveUp=Up
columnMoveDown=Down
columnMoveDown=Down
@@ -60,7 +60,7 @@ leg=L
distanceInMeters=Distanz/m
averageSpeedInKnots=Durchschn. FüG/kn
rankGain=Platz +/-
sapSailingAnalytics=SAP Sailing Analytics
sapSailingAnalytics=Sailing Analytics
leaderboard=ERGEBNISSE
leaderboardSettings=Einstellungen Ergebnisliste
selectLegDetails=Detaileinstellungen
@@ -101,4 +101,4 @@ timing=Timing
raceDetailsToShow=Renn Details
legDetailsToShow=Schenkel Details
columnMoveUp=Hoch
columnMoveDown=Runter
columnMoveDown=Runter
@@ -407,6 +407,7 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
WindInfoForRaceDAO result = null;
TrackedRace trackedRace = getTrackedRace(eventName, raceName);
if (trackedRace != null) {
MillisecondsTimePoint now = MillisecondsTimePoint.now();
result = new WindInfoForRaceDAO();
result.selectedWindSourceName = trackedRace.getWindSource().name();
TimePoint from = new MillisecondsTimePoint(fromDate);
@@ -419,7 +420,7 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
windTrackInfoDAO.windFixes = new ArrayList<WindDAO>();
WindTrack windTrack = trackedRace.getWindTrack(windSource);
windTrackInfoDAO.dampeningIntervalInMilliseconds = windTrack.getMillisecondsOverWhichToAverageWind();
if (includeTrackBasedWindEstimation && windSource == WindSource.EXPEDITION) {
if (includeTrackBasedWindEstimation && windSource == trackedRace.getWindSource()) {
estimatedTrack = new WindTrackImpl(windTrack.getMillisecondsOverWhichToAverageWind());
}
Iterator<Wind> windIter = windTrack.getFixesIterator(from, /* inclusive */true);
@@ -430,7 +431,7 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
}
WindDAO windDAO = createWindDAO(wind, windTrack);
windTrackInfoDAO.windFixes.add(windDAO);
if (includeTrackBasedWindEstimation && windSource == WindSource.EXPEDITION) {
if (includeTrackBasedWindEstimation && windSource == trackedRace.getWindSource()) {
try {
Wind estimatedWindDirection = trackedRace.getEstimatedWindDirection(wind.getPosition(), wind.getTimePoint());
estimatedTrack.add(estimatedWindDirection);
@@ -441,8 +442,24 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
}
}
windTrackInfoDAOs.put(windSource.name(), windTrackInfoDAO);
// add one "track" with a single wind direction computed from the direction from the start gate to the
// first mark
WindTrack windwardMarkWindTrack = new WindTrackImpl(windTrack.getMillisecondsOverWhichToAverageWind());
windwardMarkWindTrack.add(trackedRace.getDirectionFromStartToNextMark(now));
windTrackInfoDAOs.put("COURSEBASED", createWindTrackInfoDAO(windwardMarkWindTrack));
}
if (includeTrackBasedWindEstimation && estimatedTrack != null) {
if (!estimatedTrack.getFixes().iterator().hasNext()) {
// empty wind estimation track; add at least one estimate for the current time for the start gate
Wind estimatedWindDirectionForNow;
try {
estimatedWindDirectionForNow = trackedRace.getEstimatedWindDirection(
trackedRace.getApproximatePosition(trackedRace.getRace().getCourse().getWaypoints().iterator().next(), now), now);
estimatedTrack.add(estimatedWindDirectionForNow);
} catch (NoWindException e) {
e.printStackTrace(); // well, then we just can't add a wind estimate
}
}
WindTrackInfoDAO windEstimations = createWindTrackInfoDAO(estimatedTrack);
windTrackInfoDAOs.put("ESTIMATION", windEstimations);
}