mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-28 16:36:43 +00:00
Merge branch 'master' into bug2610_lifecycle
Conflicts: java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/EditMarkPassingsPanel.java java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/WindChart.java java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/SideBySideComponentViewer.java
This commit is contained in:
Binary file not shown.
@@ -25,7 +25,7 @@ Require-Bundle: com.sap.sailing.domain,
|
||||
com.sap.sse.gwt.adminconsole,
|
||||
org.apache.commons.math;bundle-version="2.1.0"
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Import-Package: com.google.gwt.core.ext.linker,
|
||||
Import-Package: com.google.gwt.core.ext.linker;resolution:=optional,
|
||||
com.sap.sse.util,
|
||||
javax.servlet;version="3.1.0",
|
||||
javax.servlet.descriptor;version="3.1.0",
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
<?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="false"/>
|
||||
<stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9998"/>
|
||||
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
|
||||
<listEntry value="com.sap.sailing.dashboards.gwt.RibDashboard"/>
|
||||
</listAttribute>
|
||||
<booleanAttribute key="com.google.gwt.eclipse.core.SUPERDEVMODE_ENABLED" value="true"/>
|
||||
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/dashboards/RibDashboard.html"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/com.sap.sailing.dashboards.gwt"/>
|
||||
</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="true"/>
|
||||
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" path="1" type="4"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.dashboards.gwt/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.dashboards.gwt/src/main/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.dashboards.gwt"/> </runtimeClasspathEntry> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.osgi/lib/gwt-servlet.jar" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.osgi/lib/gwt-user.jar" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.moxieapps.gwt.highcharts/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt.adminconsole/src" path="3" type="2"/> "/>
|
||||
</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 -war "${project_loc:com.sap.sailing.dashboards.gwt}" -startupUrl /dashboards/RibDashboard.html -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9998 com.sap.sailing.dashboards.gwt.RibDashboard"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/>
|
||||
<?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="false"/>
|
||||
<stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9998"/>
|
||||
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
|
||||
<listEntry value="com.sap.sailing.dashboards.gwt.RibDashboard"/>
|
||||
</listAttribute>
|
||||
<booleanAttribute key="com.google.gwt.eclipse.core.SUPERDEVMODE_ENABLED" value="true"/>
|
||||
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/dashboards/RibDashboard.html"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/com.sap.sailing.dashboards.gwt"/>
|
||||
</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="true"/>
|
||||
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" path="1" type="4"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.dashboards.gwt/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.dashboards.gwt/src/main/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.dashboards.gwt"/> </runtimeClasspathEntry> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.osgi/lib/gwt-servlet.jar" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.osgi/lib/gwt-user.jar" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.moxieapps.gwt.highcharts/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt.adminconsole/src" path="3" type="2"/> "/>
|
||||
</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 -war "${project_loc:com.sap.sailing.dashboards.gwt}" -startupUrl /dashboards/RibDashboard.html -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9998 com.sap.sailing.dashboards.gwt.RibDashboard"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m"/>
|
||||
</launchConfiguration>
|
||||
</launchConfiguration>
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
<?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="false"/>
|
||||
<stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9998"/>
|
||||
<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.dashboards.gwt.RibDashboard"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/dashboards/RibDashboard.html"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/com.sap.sailing.dashboards.gwt"/>
|
||||
</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="true"/>
|
||||
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" path="1" type="4"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.dashboards.gwt/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.dashboards.gwt/src/main/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.dashboards.gwt"/> </runtimeClasspathEntry> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.osgi/lib/gwt-servlet.jar" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.osgi/lib/gwt-user.jar" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.moxieapps.gwt.highcharts/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt.adminconsole/src" path="3" type="2"/> "/>
|
||||
</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 "${project_loc:com.sap.sailing.dashboards.gwt}" -startupUrl /dashboards/RibDashboard.html -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort auto com.sap.sailing.dashboards.gwt.RibDashboard"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/>
|
||||
<?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="false"/>
|
||||
<stringAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT" value="9998"/>
|
||||
<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.dashboards.gwt.RibDashboard"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/dashboards/RibDashboard.html"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/com.sap.sailing.dashboards.gwt"/>
|
||||
</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="true"/>
|
||||
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" path="1" type="4"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.dashboards.gwt/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.dashboards.gwt/src/main/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.dashboards.gwt"/> </runtimeClasspathEntry> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.osgi/lib/gwt-servlet.jar" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.osgi/lib/gwt-user.jar" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.moxieapps.gwt.highcharts/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt.adminconsole/src" path="3" type="2"/> "/>
|
||||
</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 "${project_loc:com.sap.sailing.dashboards.gwt}" -startupUrl /dashboards/RibDashboard.html -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort auto com.sap.sailing.dashboards.gwt.RibDashboard"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.dashboards.gwt"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m"/>
|
||||
</launchConfiguration>
|
||||
</launchConfiguration>
|
||||
|
||||
+5
-4
@@ -47,12 +47,13 @@ public final class StartAnalysisDTOFactory extends AbstractStartAnalysisCreation
|
||||
List<MarkPassing> markPassingsInOrder = convertMarkpPassingsIteratorToList(trackedRace.getMarkPassingsInOrder(secondWaypoint).iterator());
|
||||
boolean isCompetitorNullOrOneOfFirstThreeCompetitors = false;
|
||||
for (int i = 0; i < MINIMUM_MARKPASSIINGS_AT_FIRST_MARK; i++) {
|
||||
competitors.add(createStartAnalysisCompetitorDTO(dashboardDispatchContext, trackedRace, i + 1, markPassingsInOrder.get(i).getCompetitor()));
|
||||
if (competitor == null || markPassingsInOrder.get(i).getCompetitor().equals(competitor)) {
|
||||
isCompetitorNullOrOneOfFirstThreeCompetitors = true;
|
||||
if (markPassingsInOrder.size() - 1 >= i) {
|
||||
competitors.add(createStartAnalysisCompetitorDTO(dashboardDispatchContext, trackedRace, i+1, markPassingsInOrder.get(i).getCompetitor()));
|
||||
if (competitor == null || markPassingsInOrder.get(i).getCompetitor().equals(competitor)) {
|
||||
isCompetitorNullOrOneOfFirstThreeCompetitors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (competitor != null && !isCompetitorNullOrOneOfFirstThreeCompetitors) {
|
||||
int rankOfCompetitorWhilePassingSecondWaypoint = getRankOfCompetitorWhilePassingSecondWaypoint(competitor, trackedRace);
|
||||
competitors.add(createStartAnalysisCompetitorDTO(dashboardDispatchContext, trackedRace, rankOfCompetitorWhilePassingSecondWaypoint, competitor));
|
||||
|
||||
+16
@@ -79,4 +79,20 @@ public abstract class AbstractDistance implements Distance {
|
||||
public Distance add(Distance d) {
|
||||
return new NauticalMileDistance(getNauticalMiles()+d.getNauticalMiles());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double divide(Distance other) {
|
||||
return getMeters() / other.getMeters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance abs() {
|
||||
final Distance result;
|
||||
if (getMeters() >= 0) {
|
||||
result = this;
|
||||
} else {
|
||||
result = scale(-1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -164,7 +164,8 @@ public class AbstractPosition implements Position {
|
||||
@Override
|
||||
public Distance getDistanceToLine(Position left, Position right) {
|
||||
final Distance result;
|
||||
final int factor = this.crossTrackError(left, left.getBearingGreatCircle(right)).getMeters()>0?1:-1;
|
||||
final Distance crossTrackError = this.crossTrackError(left, left.getBearingGreatCircle(right));
|
||||
final int factor = crossTrackError.getMeters()>0?1:-1;
|
||||
double toLeft = Math.abs(left.getBearingGreatCircle(this).getDifferenceTo(left.getBearingGreatCircle(right))
|
||||
.getDegrees());
|
||||
double toRight = Math.abs(right.getBearingGreatCircle(this).getDifferenceTo(right.getBearingGreatCircle(left))
|
||||
@@ -172,9 +173,9 @@ public class AbstractPosition implements Position {
|
||||
if (toLeft > 90) {
|
||||
result = this.getDistance(left).scale(factor);
|
||||
} else if (toRight > 90) {
|
||||
result = this.getDistance(right).scale(factor);
|
||||
} else {
|
||||
result = this.crossTrackError(left, left.getBearingGreatCircle(right));
|
||||
result = this.getDistance(right).scale(factor);
|
||||
} else {
|
||||
result = crossTrackError;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,22 @@ public interface Distance extends Comparable<Distance>, Serializable {
|
||||
public String toString() {
|
||||
return "0m";
|
||||
}
|
||||
|
||||
@Override
|
||||
public double divide(Distance other) {
|
||||
final double result;
|
||||
if (other.getMeters() == 0) {
|
||||
result = 0./0.;
|
||||
} else {
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance abs() {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
double getGeographicalMiles();
|
||||
@@ -108,4 +124,16 @@ public interface Distance extends Comparable<Distance>, Serializable {
|
||||
Speed inTime(Duration duration);
|
||||
|
||||
Distance add(Distance d);
|
||||
|
||||
/**
|
||||
* Divides this distance by the {@code other} distance. Should the {@code other} distance
|
||||
* be {@link #<code>null</code>}, the result will be {@code NaN}.
|
||||
*/
|
||||
double divide(Distance other);
|
||||
|
||||
/**
|
||||
* If this distance is negative, returns a positive difference with the same absolute value; for positive
|
||||
* distances, this distance is returned.
|
||||
*/
|
||||
Distance abs();
|
||||
}
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
public enum FixType { BUOY }
|
||||
+1
@@ -14,6 +14,7 @@ package com.sap.sailing.domain.common;
|
||||
public enum PassingInstruction {
|
||||
None(1, 2),
|
||||
Port(1),
|
||||
Single_Unknown(1),
|
||||
Starboard(1),
|
||||
Gate(2),
|
||||
Line(2),
|
||||
|
||||
@@ -69,8 +69,8 @@ public interface Position extends Serializable {
|
||||
Distance alongTrackDistance(Position from, Bearing bearing);
|
||||
|
||||
/**
|
||||
* Computes the distance from this position to the line between <code>left<code> and <code>right<code>. This distance
|
||||
* is positive if <code>left<code> is actually the position farther to the left, as seen from this position.
|
||||
* Computes the distance from this position to the line between <code>left</code> and <code>right</code>. This distance
|
||||
* is positive if <code>left</code> is actually the position farther to the left, as seen from this position.
|
||||
*/
|
||||
Distance getDistanceToLine(Position left, Position right);
|
||||
|
||||
|
||||
+2
-1
@@ -28,6 +28,7 @@ public enum Permission implements com.sap.sse.security.shared.Permission {
|
||||
MANAGE_USERS,
|
||||
MANAGE_FILE_STORAGE,
|
||||
MANAGE_MARK_PASSINGS,
|
||||
MANAGE_MARK_POSITIONS,
|
||||
CAN_REPLAY_DURING_LIVE_RACES,
|
||||
|
||||
// back-end permissions
|
||||
@@ -35,7 +36,7 @@ public enum Permission implements com.sap.sse.security.shared.Permission {
|
||||
REGATTA,
|
||||
LEADERBOARD,
|
||||
LEADERBOARD_GROUP,
|
||||
TRACKED_RACE
|
||||
TRACKED_RACE,
|
||||
;
|
||||
|
||||
// TODO once we can use Java8 here, move this up into a "default" method on the Permission interface
|
||||
|
||||
+1
@@ -19,6 +19,7 @@ public class SailingPermissionsForRoleProvider implements PermissionsForRoleProv
|
||||
// RaceBoard:
|
||||
Permission.MANAGE_MEDIA,
|
||||
Permission.MANAGE_MARK_PASSINGS,
|
||||
Permission.MANAGE_MARK_POSITIONS,
|
||||
|
||||
// AdminConsole:
|
||||
Permission.MANAGE_ALL_COMPETITORS,
|
||||
|
||||
+4
-2
@@ -19,6 +19,7 @@ import org.junit.rules.ExpectedException;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartOfTrackingEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogRegisterCompetitorEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogUseCompetitorsFromRaceLogEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
@@ -158,7 +159,6 @@ public class CreateAndTrackWithRaceLogTest {
|
||||
@Test
|
||||
public void canDenote_Add_Track() throws MalformedURLException, FileNotFoundException, URISyntaxException,
|
||||
Exception {
|
||||
|
||||
RaceColumn column = leaderboard.getRaceColumnByName(columnName);
|
||||
RegattaLog regattaLog = leaderboard.getRegattaLike().getRegattaLog();
|
||||
RaceLog raceLog = column.getRaceLog(fleet);
|
||||
@@ -176,10 +176,11 @@ public class CreateAndTrackWithRaceLogTest {
|
||||
addFixes0(dev1);
|
||||
raceLog.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(t(), author, t(), UUID.randomUUID(), 0));
|
||||
raceLog.add(new RaceLogRegisterCompetitorEventImpl(t(), author, 0, comp1));
|
||||
raceLog.add(new RaceLogStartOfTrackingEventImpl(t(0), author, /* passId */ 0));
|
||||
// start tracking
|
||||
adapter.startTracking(service, leaderboard, column, fleet);
|
||||
|
||||
// now there is a trackedrace
|
||||
// now there is a tracked race
|
||||
TrackedRace race = column.getTrackedRace(fleet);
|
||||
assertNotNull(race);
|
||||
|
||||
@@ -211,6 +212,7 @@ public class CreateAndTrackWithRaceLogTest {
|
||||
t(0), t(10)));
|
||||
addFixes0(dev1);
|
||||
regattaLog.add(new RegattaLogRegisterCompetitorEventImpl(t(), t(), author, UUID.randomUUID(), comp1));
|
||||
raceLog.add(new RaceLogStartOfTrackingEventImpl(t(0), author, /* passId */ 0));
|
||||
|
||||
// start tracking
|
||||
adapter.startTracking(service, leaderboard, column, fleet);
|
||||
|
||||
+19
-50
@@ -13,6 +13,8 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLog;
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLogCourseDesignChangedEvent;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLogEndOfTrackingEvent;
|
||||
@@ -20,7 +22,9 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.LastPublishedCourseDesignFinder;
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.TrackingTimesFinder;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.BaseRaceLogEventVisitor;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartOfTrackingEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogDenoteForTrackingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogStartTrackingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceInformationFinder;
|
||||
@@ -107,6 +111,8 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
|
||||
private final Map<DeviceIdentifier, List<DeviceMapping<Competitor>>> competitorMappingsByDevices = new HashMap<>();
|
||||
|
||||
private DynamicTrackedRace trackedRace;
|
||||
private final AbstractLogEventAuthor raceLogEventAuthor = new LogEventAuthorImpl(
|
||||
RaceLogRaceTracker.class.getName(), 0);
|
||||
|
||||
private static final Logger logger = Logger.getLogger(RaceLogRaceTracker.class.getName());
|
||||
|
||||
@@ -239,50 +245,6 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
|
||||
return new RegattaLogDeviceMarkMappingFinder(params.getRegattaLog()).analyze();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use mapping time ranges to set time start and end of tracking time for race
|
||||
*/
|
||||
private void updateStartAndEndOfTracking() {
|
||||
// TODO bug 3196: this would need to be evaluated lazily in TrackedRaceImpl.getStartOfTracking / getEndOfTracking
|
||||
final Pair<TimePoint, TimePoint> trackingTimesFromRaceLog = trackedRace.getTrackingTimesFromRaceLogs();
|
||||
if (trackingTimesFromRaceLog == null) {
|
||||
TimePoint earliestMappingStart = new MillisecondsTimePoint(Long.MAX_VALUE);
|
||||
TimePoint latestMappingEnd = new MillisecondsTimePoint(Long.MIN_VALUE);
|
||||
for (List<? extends DeviceMapping<?>> list : competitorMappings.values()) {
|
||||
for (DeviceMapping<?> mapping : list) {
|
||||
earliestMappingStart = getUpdatedEarliestMappingStart(earliestMappingStart, mapping.getTimeRange().from());
|
||||
latestMappingEnd = getUpdatedLatestMappingEnd(latestMappingEnd, mapping.getTimeRange().to());
|
||||
}
|
||||
}
|
||||
for (List<? extends DeviceMapping<?>> list : markMappings.values()) {
|
||||
for (DeviceMapping<?> mapping : list) {
|
||||
earliestMappingStart = getUpdatedEarliestMappingStart(earliestMappingStart, mapping.getTimeRange().from());
|
||||
latestMappingEnd = getUpdatedLatestMappingEnd(latestMappingEnd, mapping.getTimeRange().to());
|
||||
}
|
||||
}
|
||||
trackedRace.setStartOfTrackingReceived(earliestMappingStart == null ? TimePoint.BeginningOfTime : earliestMappingStart);
|
||||
trackedRace.setEndOfTrackingReceived(latestMappingEnd == null ? TimePoint.EndOfTime : latestMappingEnd);
|
||||
}
|
||||
}
|
||||
|
||||
private TimePoint getUpdatedEarliestMappingStart(TimePoint earliestMappingStart, final TimePoint from) {
|
||||
if (from == null) {
|
||||
earliestMappingStart = null; // no further updates; one open interval means startOfTracking shall be the beginning of time
|
||||
} else if (earliestMappingStart != null && from.before(earliestMappingStart)) {
|
||||
earliestMappingStart = from;
|
||||
}
|
||||
return earliestMappingStart;
|
||||
}
|
||||
|
||||
private TimePoint getUpdatedLatestMappingEnd(TimePoint latestMappingEnd, final TimePoint to) {
|
||||
if (to == null) {
|
||||
latestMappingEnd = null; // no further updates; one open interval means startOfTracking shall be the beginning of time
|
||||
} else if (latestMappingEnd != null && to.after(latestMappingEnd)) {
|
||||
latestMappingEnd = to;
|
||||
}
|
||||
return latestMappingEnd;
|
||||
}
|
||||
|
||||
private <ItemT extends WithID, FixT extends GPSFix> boolean hasMappingAlreadyBeenLoaded(
|
||||
DeviceMapping<ItemT> newMapping, List<DeviceMapping<ItemT>> oldMappings) {
|
||||
if (oldMappings == null) {
|
||||
@@ -354,8 +316,6 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
|
||||
|
||||
markMappingsByDevices.clear();
|
||||
markMappingsByDevices.putAll(transformToMappingsByDevice(markMappings));
|
||||
|
||||
updateStartAndEndOfTracking();
|
||||
}
|
||||
|
||||
private void onDeviceMarkMappingEvent(RegattaLogDeviceMarkMappingEvent event) {
|
||||
@@ -429,7 +389,6 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
|
||||
competitorMappings.putAll(newMappings);
|
||||
competitorMappingsByDevices.clear();
|
||||
competitorMappingsByDevices.putAll(transformToMappingsByDevice(competitorMappings));
|
||||
updateStartAndEndOfTracking();
|
||||
}
|
||||
|
||||
private void onStartTrackingEvent(RaceLogStartTrackingEvent event) {
|
||||
@@ -439,11 +398,15 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
|
||||
}
|
||||
|
||||
private void onStartOfTrackingEvent(RaceLogStartOfTrackingEvent event) {
|
||||
trackedRace.setStartOfTrackingReceived(event.getLogicalTimePoint());
|
||||
if (trackedRace != null) {
|
||||
trackedRace.setStartOfTrackingReceived(event.getLogicalTimePoint());
|
||||
}
|
||||
}
|
||||
|
||||
private void onEndOfTrackingEvent(RaceLogEndOfTrackingEvent event) {
|
||||
trackedRace.setEndOfTrackingReceived(event.getLogicalTimePoint());
|
||||
if (trackedRace != null) {
|
||||
trackedRace.setEndOfTrackingReceived(event.getLogicalTimePoint());
|
||||
}
|
||||
}
|
||||
|
||||
private void onCourseDesignChangedEvent(RaceLogCourseDesignChangedEvent event) {
|
||||
@@ -466,6 +429,12 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
|
||||
RaceColumn raceColumn = params.getRaceColumn();
|
||||
Fleet fleet = params.getFleet();
|
||||
RaceLogDenoteForTrackingEvent denoteEvent = new RaceInformationFinder(raceLog).analyze();
|
||||
final Pair<TimePoint, TimePoint> trackingTimes = new TrackingTimesFinder(raceLog).analyze();
|
||||
if (trackingTimes == null) {
|
||||
// seems the first time tracking for this race is started; enter "now" as start of tracking
|
||||
// into the race log
|
||||
raceLog.add(new RaceLogStartOfTrackingEventImpl(MillisecondsTimePoint.now(), raceLogEventAuthor, /* passId */ 0));
|
||||
}
|
||||
BoatClass boatClass = denoteEvent.getBoatClass();
|
||||
String raceName = denoteEvent.getRaceName();
|
||||
CourseBase courseBase = new LastPublishedCourseDesignFinder(raceLog).analyze();
|
||||
@@ -473,7 +442,7 @@ public class RaceLogRaceTracker implements RaceTracker, GPSFixReceivedListener {
|
||||
courseBase = new CourseDataImpl("Default course for " + raceName);
|
||||
logger.log(Level.FINE, "Using empty course in creation of race " + raceName);
|
||||
}
|
||||
Course course = new CourseImpl(raceName + " course", courseBase.getWaypoints());
|
||||
final Course course = new CourseImpl(raceName + " course", courseBase.getWaypoints());
|
||||
if (raceColumn.getTrackedRace(fleet) != null) {
|
||||
if (event != null) {
|
||||
try {
|
||||
|
||||
+3
-2
@@ -240,8 +240,9 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
public void pingMark(RegattaLog log, Mark mark, GPSFix gpsFix, RacingEventService service) {
|
||||
final PingDeviceIdentifierImpl device = new PingDeviceIdentifierImpl();
|
||||
final TimePoint timePoint = gpsFix.getTimePoint();
|
||||
final RegattaLogEvent mapping = new RegattaLogDeviceMarkMappingEventImpl(timePoint,
|
||||
timePoint, service.getServerAuthor(), UUID.randomUUID(), mark, device, timePoint, timePoint);
|
||||
final TimePoint now = MillisecondsTimePoint.now();
|
||||
final RegattaLogEvent mapping = new RegattaLogDeviceMarkMappingEventImpl(now,
|
||||
now, service.getServerAuthor(), UUID.randomUUID(), mark, device, timePoint, timePoint);
|
||||
log.add(mapping);
|
||||
try {
|
||||
service.getGPSFixStore().storeFix(device, gpsFix);
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.sap.sailing.domain.regattalog.tracking.analyzing.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.shared.analyzing.CompetitorDeregistrator;
|
||||
import com.sap.sailing.domain.abstractlog.shared.analyzing.CompetitorsInLogAnalyzer;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.impl.CompetitorImpl;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class CompetitorDeregistrationTest extends AbstractRegattaLogTrackingTest {
|
||||
private final Competitor competitor = new CompetitorImpl("comp", "Comp", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null);
|
||||
private final Competitor competitor2 = new CompetitorImpl("comp2", "Comp2", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null);
|
||||
|
||||
@Test
|
||||
public void testCompetitorDeregistration() {
|
||||
log.add(new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), author, competitor));
|
||||
log.add(new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), author, competitor2));
|
||||
final CompetitorDeregistrator<RegattaLog, RegattaLogEvent, RegattaLogEventVisitor> deregistrator = new CompetitorDeregistrator<>(log,
|
||||
Collections.singleton(competitor2), author);
|
||||
final Set<RegattaLogEvent> events = deregistrator.analyze();
|
||||
deregistrator.deregister(events);
|
||||
final Set<Competitor> competitors = new CompetitorsInLogAnalyzer<>(log).analyze();
|
||||
assertEquals(1, competitors.size());
|
||||
assertSame(competitor, competitors.iterator().next());
|
||||
}
|
||||
}
|
||||
+223
@@ -1,9 +1,11 @@
|
||||
package com.sap.sailing.domain.regattalog.tracking.analyzing.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import junit.framework.Assert;
|
||||
@@ -17,6 +19,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCo
|
||||
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceCompetitorMappingFinder;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.impl.CompetitorImpl;
|
||||
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
|
||||
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
|
||||
@@ -55,6 +58,226 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
log.add(mapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expecting that a single, closed mapping interval spanning a fix will be cut into two intervals excluding the fix time point
|
||||
*/
|
||||
@Test
|
||||
public void testFixRemovalForMiddleOfInterval() throws NotRevokableException {
|
||||
final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log);
|
||||
addMapping(author, device, 100l, 200l, competitor);
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertEquals(1, mappings.size());
|
||||
assertTrue(mappings.containsKey(competitor));
|
||||
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(150));
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertEquals(1, mappings.size());
|
||||
assertTrue(mappings.containsKey(competitor));
|
||||
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(2, deviceMappings.size());
|
||||
boolean found100 = false;
|
||||
for (final DeviceMapping<Competitor> mapping : deviceMappings) {
|
||||
if (mapping.getTimeRange().from().asMillis() == 100) {
|
||||
found100 = true;
|
||||
assertEquals(149, mapping.getTimeRange().to().asMillis());
|
||||
} else {
|
||||
assertEquals(151, mapping.getTimeRange().from().asMillis());
|
||||
assertEquals(200, mapping.getTimeRange().to().asMillis());
|
||||
}
|
||||
}
|
||||
assertTrue(found100);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFixRemovalForMiddleOfIntervalClosedSeparately() throws NotRevokableException {
|
||||
final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log);
|
||||
final Serializable mappingId = addMapping(author, device, 100l, null, competitor);
|
||||
closeMapping(author, device, mappingId, 200);
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertEquals(1, mappings.size());
|
||||
assertTrue(mappings.containsKey(competitor));
|
||||
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(150));
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertEquals(1, mappings.size());
|
||||
assertTrue(mappings.containsKey(competitor));
|
||||
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(2, deviceMappings.size());
|
||||
for (final DeviceMapping<Competitor> mapping : deviceMappings) {
|
||||
if (mapping.getTimeRange().from().asMillis() == 100) {
|
||||
assertEquals(149, mapping.getTimeRange().to().asMillis());
|
||||
} else {
|
||||
assertEquals(151, mapping.getTimeRange().from().asMillis());
|
||||
assertEquals(200, mapping.getTimeRange().to().asMillis());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFixRemovalForStartOfInterval() throws NotRevokableException {
|
||||
final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log);
|
||||
addMapping(author, device, 100l, 200l, competitor);
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertEquals(1, mappings.size());
|
||||
assertTrue(mappings.containsKey(competitor));
|
||||
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100));
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertEquals(1, mappings.size());
|
||||
assertTrue(mappings.containsKey(competitor));
|
||||
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
assertEquals(101, deviceMappings.get(0).getTimeRange().from().asMillis());
|
||||
assertEquals(200, deviceMappings.get(0).getTimeRange().to().asMillis());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFixRemovalForStartOfIntervalClosedSeparately() throws NotRevokableException {
|
||||
final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log);
|
||||
final Serializable mappingId = addMapping(author, device, 100l, null, competitor);
|
||||
closeMapping(author, device, mappingId, 200);
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertEquals(1, mappings.size());
|
||||
assertTrue(mappings.containsKey(competitor));
|
||||
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100));
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertEquals(1, mappings.size());
|
||||
assertTrue(mappings.containsKey(competitor));
|
||||
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
assertEquals(101, deviceMappings.get(0).getTimeRange().from().asMillis());
|
||||
assertEquals(200, deviceMappings.get(0).getTimeRange().to().asMillis());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFixRemovalForEndOfInterval() throws NotRevokableException {
|
||||
final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log);
|
||||
addMapping(author, device, 100l, 200l, competitor);
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertEquals(1, mappings.size());
|
||||
assertTrue(mappings.containsKey(competitor));
|
||||
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(200));
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertEquals(1, mappings.size());
|
||||
assertTrue(mappings.containsKey(competitor));
|
||||
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
assertEquals(100, deviceMappings.get(0).getTimeRange().from().asMillis());
|
||||
assertEquals(199, deviceMappings.get(0).getTimeRange().to().asMillis());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFixRemovalForEndOfIntervalClosedSeparately() throws NotRevokableException {
|
||||
final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log);
|
||||
final Serializable mappingId = addMapping(author, device, 100l, null, competitor);
|
||||
closeMapping(author, device, mappingId, 200);
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertEquals(1, mappings.size());
|
||||
assertTrue(mappings.containsKey(competitor));
|
||||
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(200));
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertEquals(1, mappings.size());
|
||||
assertTrue(mappings.containsKey(competitor));
|
||||
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
assertEquals(100, deviceMappings.get(0).getTimeRange().from().asMillis());
|
||||
assertEquals(199, deviceMappings.get(0).getTimeRange().to().asMillis());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFixRemovalForMillisecondInterval() throws NotRevokableException {
|
||||
final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log);
|
||||
addMapping(author, device, 100l, 100l, competitor);
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertEquals(1, mappings.size());
|
||||
assertTrue(mappings.containsKey(competitor));
|
||||
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100));
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertTrue(mappings.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFixRemovalForMillisecondIntervalClosedSeparately() throws NotRevokableException {
|
||||
final RegattaLogDeviceCompetitorMappingFinder finder = new RegattaLogDeviceCompetitorMappingFinder(log);
|
||||
final Serializable mappingId = addMapping(author, device, 100l, null, competitor);
|
||||
closeMapping(author, device, mappingId, 100);
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertEquals(1, mappings.size());
|
||||
assertTrue(mappings.containsKey(competitor));
|
||||
final List<DeviceMapping<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMapping<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100));
|
||||
{
|
||||
final Map<Competitor, List<DeviceMapping<Competitor>>> mappings = finder.analyze();
|
||||
assertTrue(mappings.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notDisturbedByMappingsForOtherItemsAndDevices() {
|
||||
//two mappings for first competitor found? (competitor2 mappings should not "distract")
|
||||
|
||||
+14
@@ -1,9 +1,16 @@
|
||||
package com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorMappingEventImpl;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class RegattaLogDeviceCompetitorMappingFinder extends RegattaLogDeviceMappingFinder<Competitor> {
|
||||
|
||||
@@ -15,4 +22,11 @@ public class RegattaLogDeviceCompetitorMappingFinder extends RegattaLogDeviceMap
|
||||
protected boolean isValidMapping(RegattaLogDeviceMappingEvent<?> mapping) {
|
||||
return mapping instanceof RegattaLogDeviceCompetitorMappingEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RegattaLogDeviceCompetitorMappingEvent createDeviceMappingEvent(Competitor item, AbstractLogEventAuthor author,
|
||||
TimePoint from, TimePoint to, DeviceIdentifier deviceId) {
|
||||
final TimePoint now = MillisecondsTimePoint.now();
|
||||
return new RegattaLogDeviceCompetitorMappingEventImpl(now, now, author, UUID.randomUUID(), item, deviceId, from, to);
|
||||
}
|
||||
}
|
||||
|
||||
+52
-11
@@ -8,11 +8,13 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
|
||||
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
|
||||
import com.sap.sailing.domain.racelogtracking.impl.DeviceMappingImpl;
|
||||
@@ -34,7 +36,7 @@ import com.sap.sse.common.impl.TimeRangeImpl;
|
||||
* {@code DeviceMapping}, the only changes being introduced by closing open time ranges in cases where an
|
||||
* according {@code CloseOpenEndedDeviceMappingEvent} exists.
|
||||
*/
|
||||
public class RegattaLogDeviceMappingFinder<ItemT extends WithID> extends RegattaLogAnalyzer<Map<ItemT, List<DeviceMapping<ItemT>>>> {
|
||||
public abstract class RegattaLogDeviceMappingFinder<ItemT extends WithID> extends RegattaLogAnalyzer<Map<ItemT, List<DeviceMapping<ItemT>>>> {
|
||||
private static Logger logger = Logger.getLogger(RegattaLogDeviceMappingFinder.class.getName());
|
||||
|
||||
public RegattaLogDeviceMappingFinder(RegattaLog log) {
|
||||
@@ -51,41 +53,46 @@ public class RegattaLogDeviceMappingFinder<ItemT extends WithID> extends Regatta
|
||||
Collections.singletonList(originalEventId));
|
||||
}
|
||||
|
||||
private <T, U> List<U> getItemSet(Map<T, List<U>> map, T item) {
|
||||
List<U> list = map.get(item);
|
||||
private List<RegattaLogDeviceMappingEvent<ItemT>> getMappingEventsForItem(Map<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>> map, ItemT item) {
|
||||
List<RegattaLogDeviceMappingEvent<ItemT>> list = map.get(item);
|
||||
if (list == null) {
|
||||
list = new ArrayList<U>();
|
||||
list = new ArrayList<RegattaLogDeviceMappingEvent<ItemT>>();
|
||||
map.put(item, list);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<ItemT, List<DeviceMapping<ItemT>>> performAnalysis() {
|
||||
Map<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>> events = new HashMap<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>>();
|
||||
Map<ItemT, List<DeviceMapping<ItemT>>> mappings = new HashMap<ItemT, List<DeviceMapping<ItemT>>>();
|
||||
Map<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent> closingEvents = new HashMap<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent>();
|
||||
findUnrevokedMappingAndClosingEvents(events, closingEvents);
|
||||
Map<ItemT, List<DeviceMapping<ItemT>>> mappings = new HashMap<ItemT, List<DeviceMapping<ItemT>>>();
|
||||
for (ItemT item : events.keySet()) {
|
||||
mappings.put(item, closeOpenRanges(events.get(item), item, closingEvents));
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
|
||||
private void findUnrevokedMappingAndClosingEvents(Map<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>> events,
|
||||
Map<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent> closingEvents) {
|
||||
for (RegattaLogEvent e : getLog().getUnrevokedEvents()) {
|
||||
if (e instanceof RegattaLogDeviceMappingEvent && isValidMapping(((RegattaLogDeviceMappingEvent<?>) e))) {
|
||||
@SuppressWarnings("unchecked")
|
||||
RegattaLogDeviceMappingEvent<ItemT> mappingEvent = (RegattaLogDeviceMappingEvent<ItemT>) e;
|
||||
getItemSet(events, mappingEvent.getMappedTo()).add(mappingEvent);
|
||||
getMappingEventsForItem(events, mappingEvent.getMappedTo()).add(mappingEvent);
|
||||
} else if (e instanceof RegattaLogCloseOpenEndedDeviceMappingEvent) {
|
||||
//a higher priority closing events for the same mapping event overwrites the lower priority one
|
||||
RegattaLogCloseOpenEndedDeviceMappingEvent closingEvent = (RegattaLogCloseOpenEndedDeviceMappingEvent) e;
|
||||
closingEvents.put(closingEvent.getDeviceMappingEventId(), closingEvent);
|
||||
}
|
||||
}
|
||||
for (ItemT item : events.keySet()) {
|
||||
mappings.put(item, closeOpenRanges(events.get(item), item, closingEvents));
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
|
||||
private List<DeviceMapping<ItemT>> closeOpenRanges(List<RegattaLogDeviceMappingEvent<ItemT>> events, ItemT item,
|
||||
Map<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent> closingEvents) {
|
||||
List<DeviceMapping<ItemT>> result = new ArrayList<DeviceMapping<ItemT>>();
|
||||
for (RegattaLogDeviceMappingEvent<ItemT> event : events) {
|
||||
for (final RegattaLogDeviceMappingEvent<ItemT> event : events) {
|
||||
TimePoint from = event.getFrom();
|
||||
TimePoint to = event.getTo();
|
||||
TimePoint closingTimePoint = closingEvents.containsKey(event.getId()) ? closingEvents.get(event.getId())
|
||||
@@ -100,4 +107,38 @@ public class RegattaLogDeviceMappingFinder<ItemT extends WithID> extends Regatta
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* By revoking existing mappings for {@code item} and replacing them by mappings that exclude {@code fixTimePoint}, any
|
||||
* fix at {@code fixTimePoint} will no longer be linked to {@code item}. If a mapping is limited to the exact fix time point,
|
||||
* the mapping is only revoked and not replaced. If one boundary is exactly the fix time point, the interval is revoked and
|
||||
* replaced by a single interval with that border adjusted so it excludes the fix.
|
||||
*/
|
||||
public void removeTimePointFromMapping(ItemT item, TimePoint fixTimePoint) throws NotRevokableException {
|
||||
Map<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>> events = new HashMap<ItemT, List<RegattaLogDeviceMappingEvent<ItemT>>>();
|
||||
Map<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent> closingEvents = new HashMap<Serializable, RegattaLogCloseOpenEndedDeviceMappingEvent>();
|
||||
findUnrevokedMappingAndClosingEvents(events, closingEvents);
|
||||
for (final RegattaLogDeviceMappingEvent<ItemT> event : events.get(item)) {
|
||||
final TimePoint from = event.getFrom();
|
||||
final RegattaLogCloseOpenEndedDeviceMappingEvent closingEvent = closingEvents.get(event.getId());
|
||||
final TimePoint to = closingEvent!= null ? closingEvent.getClosingTimePoint() : event.getTo();
|
||||
final TimeRange mappingTimeRange = new TimeRangeImpl(from, to);
|
||||
if (mappingTimeRange.includes(fixTimePoint)) {
|
||||
if (closingEvent != null) {
|
||||
log.revokeEvent(closingEvent.getAuthor(), closingEvent, "removing single time point "+fixTimePoint+" from mapping for "+item);
|
||||
}
|
||||
log.revokeEvent(event.getAuthor(), event, "removing single time point "+fixTimePoint+" from mapping for "+item);
|
||||
final TimePoint endOfFirstHalf = fixTimePoint.minus(1);
|
||||
final TimePoint startOfSecondHalf = fixTimePoint.plus(1);
|
||||
if (!endOfFirstHalf.before(from)) {
|
||||
log.add(createDeviceMappingEvent(item, event.getAuthor(), from, endOfFirstHalf, event.getDevice()));
|
||||
}
|
||||
if (!to.before(startOfSecondHalf)) {
|
||||
log.add(createDeviceMappingEvent(item, event.getAuthor(), startOfSecondHalf, to, event.getDevice()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract RegattaLogDeviceMappingEvent<ItemT> createDeviceMappingEvent(ItemT item, AbstractLogEventAuthor author, TimePoint plus, TimePoint to, DeviceIdentifier deviceId);
|
||||
}
|
||||
|
||||
+14
@@ -1,9 +1,16 @@
|
||||
package com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceMarkMappingEventImpl;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class RegattaLogDeviceMarkMappingFinder extends RegattaLogDeviceMappingFinder<Mark> {
|
||||
|
||||
@@ -15,4 +22,11 @@ public class RegattaLogDeviceMarkMappingFinder extends RegattaLogDeviceMappingFi
|
||||
protected boolean isValidMapping(RegattaLogDeviceMappingEvent<?> mapping) {
|
||||
return mapping instanceof RegattaLogDeviceMarkMappingEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RegattaLogDeviceMarkMappingEvent createDeviceMappingEvent(Mark item, AbstractLogEventAuthor author,
|
||||
TimePoint from, TimePoint to, DeviceIdentifier deviceId) {
|
||||
final TimePoint now = MillisecondsTimePoint.now();
|
||||
return new RegattaLogDeviceMarkMappingEventImpl(now, now, author, UUID.randomUUID(), item, deviceId, from, to);
|
||||
}
|
||||
}
|
||||
|
||||
+27
-14
@@ -1,6 +1,7 @@
|
||||
package com.sap.sailing.domain.abstractlog.shared.analyzing;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -14,14 +15,21 @@ import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
* Generates events that close the events that are responsible for the {@link #mapping} that is passed to the
|
||||
* constructor.
|
||||
* Finds the competitor registration events for a given set of {@link Competitor competitors} that must be
|
||||
* {@link AbstractLog#revokeEvent(AbstractLogEventAuthor, AbstractLogEvent) revoked} in order to deregister the
|
||||
* competitors. Note that the de-registration cannot be performed during analysis because
|
||||
* {@link BaseLogAnalyzer#analyze()} obtains a read lock on the log whereas
|
||||
* {@link AbstractLog#revokeEvent(AbstractLogEventAuthor, AbstractLogEvent)} requires a write lock, and upgrading from a
|
||||
* read to a write lock is not possible.<p>
|
||||
*
|
||||
* Clients must call {@link #deregister} with the result of {@link #analyze()} in order to actually perform the
|
||||
* deregistration.
|
||||
*
|
||||
* @author Jan Bross
|
||||
*
|
||||
*/
|
||||
public class CompetitorDeregistrator<LogT extends AbstractLog<EventT, VisitorT>, EventT extends AbstractLogEvent<VisitorT>, VisitorT>
|
||||
extends BaseLogAnalyzer<LogT, EventT, VisitorT, Void> {
|
||||
extends BaseLogAnalyzer<LogT, EventT, VisitorT, Set<EventT>> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(CompetitorDeregistrator.class.getName());
|
||||
protected final Iterable<Competitor> competitorsToDeregister;
|
||||
@@ -34,24 +42,29 @@ extends BaseLogAnalyzer<LogT, EventT, VisitorT, Void> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void performAnalysis() {
|
||||
HashSet<Competitor> competitorSet = new HashSet<Competitor>();
|
||||
protected Set<EventT> performAnalysis() {
|
||||
final Set<EventT> result = new HashSet<>();
|
||||
final HashSet<Competitor> competitorSet = new HashSet<Competitor>();
|
||||
Util.addAll(competitorsToDeregister, competitorSet);
|
||||
|
||||
for (EventT event : log.getUnrevokedEventsDescending()) {
|
||||
if (event instanceof RegisterCompetitorEvent) {
|
||||
RegisterCompetitorEvent<?> registerEvent = (RegisterCompetitorEvent<?>) event;
|
||||
if (competitorSet.contains(registerEvent.getCompetitor())) {
|
||||
try {
|
||||
log.revokeEvent(eventAuthor, event,
|
||||
"unregistering competitor because no longer selected for registration");
|
||||
} catch (NotRevokableException e) {
|
||||
logger.log(Level.WARNING, "could not unregister competitor by adding RevokeEvent", e);
|
||||
}
|
||||
result.add(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void deregister(final Set<EventT> competitorRegistrationEvents) {
|
||||
for (final EventT event : competitorRegistrationEvents) {
|
||||
try {
|
||||
log.revokeEvent(eventAuthor, event,
|
||||
"unregistering competitor because no longer selected for registration");
|
||||
} catch (NotRevokableException e) {
|
||||
logger.log(Level.WARNING, "could not unregister competitor by adding RevokeEvent", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -2,11 +2,14 @@ package com.sap.sailing.domain.base;
|
||||
|
||||
import com.sap.sailing.domain.common.BoatHullType;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.IsManagedByCache;
|
||||
import com.sap.sse.common.Named;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public interface BoatClass extends Named, IsManagedByCache<SharedDomainFactory> {
|
||||
final Duration APPROXIMATE_AVERAGE_MANEUVER_DURATION = Duration.ONE_SECOND.times(8); // as discussed with Dennis Gehrlein
|
||||
|
||||
/**
|
||||
* The distance returned by this method should be appropriate for use in
|
||||
* {@link TrackedRace#approximate(Competitor, Distance, TimePoint, TimePoint)} so that penalty circles and other
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ public class BoatClassImpl extends NamedImpl implements BoatClass {
|
||||
this.hullLength = hullLength;
|
||||
this.hullBeam = hullBeam;
|
||||
this.hullType = hullType;
|
||||
approximateManeuverDurationInMilliseconds = 8000; // as discussed with Dennis Gehrlein
|
||||
approximateManeuverDurationInMilliseconds = APPROXIMATE_AVERAGE_MANEUVER_DURATION.asMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+21
-4
@@ -313,19 +313,36 @@ public class TrackImpl<FixType extends Timed> implements Track<FixType> {
|
||||
}
|
||||
|
||||
protected boolean add(FixType fix) {
|
||||
return add(fix, /* replace */ false);
|
||||
}
|
||||
|
||||
protected boolean add(FixType fix, boolean replace) {
|
||||
lockForWrite();
|
||||
try {
|
||||
return addWithoutLocking(fix);
|
||||
return addWithoutLocking(fix, replace);
|
||||
} finally {
|
||||
unlockAfterWrite();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The caller must ensure to hold the write lock for this track when calling this methos
|
||||
* The caller must ensure to hold the write lock for this track when calling this method.
|
||||
*
|
||||
* @param replace
|
||||
* whether or not to replace an existing fix in the track that is equal to {@link #fix} as defined by the
|
||||
* comparator used for the {@link #fixes} set. By default this is a comparator only comparing the
|
||||
* fixes' time stamps. Subclasses may use different comparator implementations.
|
||||
*/
|
||||
protected boolean addWithoutLocking(FixType fix) {
|
||||
return getInternalRawFixes().add(fix);
|
||||
protected boolean addWithoutLocking(FixType fix, boolean replace) {
|
||||
final boolean result;
|
||||
final boolean added = getInternalRawFixes().add(fix);
|
||||
if (!added && replace) {
|
||||
getInternalRawFixes().remove(fix);
|
||||
result = getInternalRawFixes().add(fix);
|
||||
} else {
|
||||
result = added;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#Fri Jun 10 21:39:55 CEST 2011
|
||||
eclipse.preferences.version=1
|
||||
encoding//src/com/sap/sailing/domain/test/WindTest.java=UTF-8
|
||||
encoding//src/com/sap/sailing/domain/test/markpassing/StarIDM2013MarkPassingTest.java=UTF-8
|
||||
|
||||
Binary file not shown.
+4
-4
@@ -84,15 +84,15 @@ public class TimeRangeTest {
|
||||
two = create(11, 16);
|
||||
assertNull(one.union(two));
|
||||
|
||||
two = create(7, Long.MAX_VALUE);
|
||||
two = create(7, TimePoint.EndOfTime.asMillis());
|
||||
union = two.union(one);
|
||||
TimeRange union2 = one.union(two);
|
||||
assertEquals(5, union.from().asMillis());
|
||||
assertEquals(5, union2.from().asMillis());
|
||||
assertEquals(Long.MAX_VALUE, union.to().asMillis());
|
||||
assertEquals(Long.MAX_VALUE, union2.to().asMillis());
|
||||
assertEquals(TimePoint.EndOfTime.asMillis(), union.to().asMillis());
|
||||
assertEquals(TimePoint.EndOfTime.asMillis(), union2.to().asMillis());
|
||||
|
||||
one = create(Long.MIN_VALUE, 10);
|
||||
one = create(TimePoint.BeginningOfTime.asMillis(), 10);
|
||||
union = two.union(one);
|
||||
assertTrue(union.hasOpenBeginning());
|
||||
assertTrue(union.hasOpenEnd());
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
package com.sap.sailing.domain.markpassingcalculation.impl;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sailing.domain.markpassingcalculation.impl.CandidateFinderImpl.AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
|
||||
public class CandidateFinderImplWhiteBoxTest {
|
||||
private static class CandidateFinderWithPublicGetProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances extends CandidateFinderImpl {
|
||||
public CandidateFinderWithPublicGetProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances(
|
||||
DynamicTrackedRace race) {
|
||||
super(race);
|
||||
}
|
||||
|
||||
public Double getProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances(
|
||||
final List<AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine> distancesToStartLineOfOtherCompetitors, boolean startIsLine) {
|
||||
return super.getProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances(distancesToStartLineOfOtherCompetitors, startIsLine);
|
||||
}
|
||||
}
|
||||
|
||||
private CandidateFinderWithPublicGetProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances finder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
DynamicTrackedRace trackedRace = mock(DynamicTrackedRace.class);
|
||||
final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29er");
|
||||
RaceDefinition race = mock(RaceDefinition.class);
|
||||
when(trackedRace.getRace()).thenReturn(race);
|
||||
when(race.getBoatClass()).thenReturn(boatClass);
|
||||
when(race.getCompetitors()).thenReturn(Collections.emptySet());
|
||||
finder = new CandidateFinderWithPublicGetProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances(trackedRace);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneProbabilityForEmptyList() {
|
||||
List<AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine> emptyDistancesToStartLineOfOtherCompetitors = new ArrayList<>();
|
||||
final double probability = finder.getProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances(emptyDistancesToStartLineOfOtherCompetitors,
|
||||
/* startIsLine */ true);
|
||||
assertEquals(1.0, probability, 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighProbabilityForCloseProximityAndNoLine() {
|
||||
List<AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine> distancesToStartLineOfOtherCompetitors = new ArrayList<>();
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(3.0, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(5.0, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(2.0, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(8.0, null));
|
||||
final double probability = finder.getProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances(distancesToStartLineOfOtherCompetitors,
|
||||
/* startIsLine */ false);
|
||||
assertTrue(probability > 0.95);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighProbabilityForCloseProximityExceptOneAndNoLine() {
|
||||
List<AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine> distancesToStartLineOfOtherCompetitors = new ArrayList<>();
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(3.0, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(5.0, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(2.0, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(8.0, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(1.0, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(2.5, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(3.2, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(2739.0, null));
|
||||
final double probability = finder.getProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances(distancesToStartLineOfOtherCompetitors,
|
||||
/* startIsLine */ false);
|
||||
assertTrue("Expected probability to exceed 55% but got "+probability, probability > 0.55);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLowProbabilityForMostFarAwayAndNoLine() {
|
||||
List<AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine> distancesToStartLineOfOtherCompetitors = new ArrayList<>();
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(50.0, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(250.0, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(120.0, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(200.0, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(300.0, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(2.5, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(3.2, null));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(2739.0, null));
|
||||
final double probability = finder.getProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances(distancesToStartLineOfOtherCompetitors,
|
||||
/* startIsLine */ false);
|
||||
assertTrue("Expected probability to be below 10% but got "+probability, probability <= 0.1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighProbabilityForLateStartAndStartIsLine() {
|
||||
List<AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine> distancesToStartLineOfOtherCompetitors = new ArrayList<>();
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(100.0, -80.));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(120.0, -82.));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(130.0, -81.));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(90.0, -79.));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(140.0, -75.));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(150, -88.));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(135, -77.));
|
||||
distancesToStartLineOfOtherCompetitors.add(createDistancePair(20.0, -10.)); // another late starter just across the line
|
||||
final double probability = finder.getProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances(distancesToStartLineOfOtherCompetitors,
|
||||
/* startIsLine */ true);
|
||||
assertTrue("Expected probability to be above 85% but got "+probability, probability >= 0.85);
|
||||
}
|
||||
|
||||
private AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine createDistancePair(double absoluteDistanceToLine,
|
||||
Double signedXTEDistanceFromLine) {
|
||||
return new AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine(
|
||||
new MeterDistance(absoluteDistanceToLine), signedXTEDistanceFromLine == null ? null : new MeterDistance(signedXTEDistanceFromLine));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -54,7 +54,7 @@ public abstract class AbstractTracTracLiveTest extends StoredTrackBasedTest {
|
||||
private IEventSubscriber eventSubscriber;
|
||||
private IRaceSubscriber raceSubscriber;
|
||||
private final Collection<Receiver> receivers;
|
||||
|
||||
|
||||
@Rule public Timeout AbstractTracTracLiveTestTimeout = new Timeout(3 * 60 * 1000);
|
||||
|
||||
protected AbstractTracTracLiveTest() throws URISyntaxException, MalformedURLException {
|
||||
|
||||
Executable → Regular
+5
-7
@@ -256,7 +256,7 @@ public class TrackTest {
|
||||
protected Pair<GPSFixMoving, Speed> computeMaxSpeed(TimePoint from, TimePoint to) {
|
||||
Pair<GPSFixMoving, Speed> result = super.computeMaxSpeed(from, to);
|
||||
try {
|
||||
Thread.sleep(500); // just wait a bit; can't lock really because that would cause a deadlock
|
||||
Thread.sleep(1000); // just wait a bit; can't lock really because that would cause a deadlock
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -280,11 +280,9 @@ public class TrackTest {
|
||||
};
|
||||
}
|
||||
};
|
||||
new Thread(()->{
|
||||
GPSFixMoving fix1 = new GPSFixMovingImpl(new DegreePosition(0, 0), new MillisecondsTimePoint(0), new KnotSpeedWithBearingImpl(
|
||||
GPSFixMoving fix1 = new GPSFixMovingImpl(new DegreePosition(0, 0), new MillisecondsTimePoint(0), new KnotSpeedWithBearingImpl(
|
||||
1, new DegreeBearingImpl(123)));
|
||||
track.addGPSFix(fix1);
|
||||
}).start();
|
||||
track.addGPSFix(fix1);
|
||||
// The following getMaximumSpeedOverGround call will trigger a computeMaxSpeed(...) and a cache(...) call
|
||||
new Thread(()->
|
||||
assertEquals(1., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(7200000)).
|
||||
@@ -319,8 +317,8 @@ public class TrackTest {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).start();
|
||||
cacheBarrier.await(10, TimeUnit.SECONDS);
|
||||
cacheDone.await(10, TimeUnit.SECONDS);
|
||||
cacheBarrier.await(20, TimeUnit.SECONDS);
|
||||
cacheDone.await(20, TimeUnit.SECONDS);
|
||||
testDone.await();
|
||||
assertEquals(2., maxSpeed[0], 0.001);
|
||||
}
|
||||
|
||||
+49
-17
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.test.markpassing;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -11,6 +12,7 @@ import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -62,7 +64,7 @@ public abstract class AbstractMarkPassingTest extends OnlineTracTracBasedTest {
|
||||
simpleName = getClass().getSimpleName();
|
||||
}
|
||||
|
||||
private void setUp(String raceNumber) throws IOException, InterruptedException, URISyntaxException, ParseException, SubscriberInitializationException, CreateModelException {
|
||||
protected void setUp(String raceNumber) throws IOException, InterruptedException, URISyntaxException, ParseException, SubscriberInitializationException, CreateModelException {
|
||||
super.setUp();
|
||||
URI storedUri = new URI("file:///"
|
||||
+ new File("resources/" + getFileName() + raceNumber + ".mtb").getCanonicalPath().replace('\\', '/'));
|
||||
@@ -83,9 +85,26 @@ public abstract class AbstractMarkPassingTest extends OnlineTracTracBasedTest {
|
||||
givenMarkPasses.put(wp, markPassing);
|
||||
}
|
||||
givenPasses.put(c, givenMarkPasses);
|
||||
// now clear the mark passings in the TrackedRace for competitor c:
|
||||
getTrackedRace().updateMarkPassings(c, Collections.emptySet());
|
||||
}
|
||||
}
|
||||
|
||||
protected Iterable<Waypoint> getWaypoints() {
|
||||
return waypoints;
|
||||
}
|
||||
|
||||
protected Competitor getCompetitorByName(final String name) {
|
||||
Competitor result = null;
|
||||
for (Competitor c : getTrackedRace().getRace().getCompetitors()) {
|
||||
if (c.getName().equals(name)) {
|
||||
result = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract String getFileName();
|
||||
|
||||
protected void testRace(String raceNumber) throws IOException, InterruptedException, URISyntaxException, ParseException, SubscriberInitializationException, CreateModelException {
|
||||
@@ -210,29 +229,19 @@ public abstract class AbstractMarkPassingTest extends OnlineTracTracBasedTest {
|
||||
}
|
||||
|
||||
private void testMiddleOfRace(int zeroBasedIndexOfLastWaypointToBePassed) {
|
||||
int mistakes = 0;
|
||||
CandidateFinder finder = new CandidateFinderImpl(getTrackedRace());
|
||||
CandidateChooser chooser = new CandidateChooserImpl(getTrackedRace());
|
||||
int mistakes = 0;
|
||||
Waypoint lastWaypointToBePassed = waypoints.get(zeroBasedIndexOfLastWaypointToBePassed);
|
||||
Waypoint wayPointAfterwards = waypoints.get(zeroBasedIndexOfLastWaypointToBePassed + 1);
|
||||
for (Competitor c : getRace().getCompetitors()) {
|
||||
MarkPassing markPassingAfter = givenPasses.get(c).get(wayPointAfterwards);
|
||||
if (markPassingAfter != null) {
|
||||
if (givenPasses.get(c).get(lastWaypointToBePassed) != null && givenPasses.get(c).get(wayPointAfterwards) != null) {
|
||||
TimePoint beforeNextPassing = markPassingAfter.getTimePoint().minus(20000);
|
||||
List<GPSFix> fixes = new ArrayList<GPSFix>();
|
||||
try {
|
||||
getTrackedRace().getTrack(c).lockForRead();
|
||||
for (GPSFixMoving fix : getTrackedRace().getTrack(c).getFixes()) {
|
||||
if (fix.getTimePoint().before(beforeNextPassing)) {
|
||||
fixes.add(fix);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
getTrackedRace().getTrack(c).unlockAfterRead();
|
||||
}
|
||||
Util.Pair<Iterable<Candidate>, Iterable<Candidate>> f = finder.getCandidateDeltas(c, fixes);
|
||||
chooser.calculateMarkPassDeltas(c, f.getA(), f.getB());
|
||||
if (givenPasses.get(c).get(lastWaypointToBePassed) != null) {
|
||||
final TimePoint lastGivenPassingToDetectAt = givenPasses.get(c).get(lastWaypointToBePassed).getTimePoint();
|
||||
// add fixes up to 50% into the leg starting after the last passing to be detected
|
||||
final TimePoint beforeNextPassing = lastGivenPassingToDetectAt.plus(lastGivenPassingToDetectAt.until(markPassingAfter.getTimePoint()).times(0.5));
|
||||
calculateMarkPassingsForPartialTrack(c, beforeNextPassing, finder, chooser);
|
||||
boolean gotPassed = true;
|
||||
boolean gotOther = false;
|
||||
System.out.println(c);
|
||||
@@ -243,10 +252,16 @@ public abstract class AbstractMarkPassingTest extends OnlineTracTracBasedTest {
|
||||
if (waypoints.indexOf(w) <= zeroBasedIndexOfLastWaypointToBePassed) {
|
||||
if ((old == null) != (newm == null)) {
|
||||
gotPassed = false;
|
||||
fail("Waypoint "+w+" was "+(old == null?"not ":"")+"passed by "+c+" originally"
|
||||
+(old==null?"":": "+old)+"; we detected it "+(newm==null?"not ":"")+"having been passed"+
|
||||
(newm == null ? "" : (": "+newm)));
|
||||
}
|
||||
} else {
|
||||
if (w != wayPointAfterwards && newm != null) {
|
||||
gotOther = true;
|
||||
fail("Received a park passing "+newm+" for the "+waypoints.indexOf(w)+
|
||||
"th waypoint "+w+" by "+c+" although only up to "+
|
||||
zeroBasedIndexOfLastWaypointToBePassed +" were to be reported");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,6 +275,23 @@ public abstract class AbstractMarkPassingTest extends OnlineTracTracBasedTest {
|
||||
|
||||
}
|
||||
|
||||
protected void calculateMarkPassingsForPartialTrack(Competitor c, final TimePoint upToTimePoint,
|
||||
CandidateFinder finder, CandidateChooser chooser) {
|
||||
List<GPSFix> fixes = new ArrayList<GPSFix>();
|
||||
try {
|
||||
getTrackedRace().getTrack(c).lockForRead();
|
||||
for (GPSFixMoving fix : getTrackedRace().getTrack(c).getFixes()) {
|
||||
if (fix.getTimePoint().before(upToTimePoint)) {
|
||||
fixes.add(fix);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
getTrackedRace().getTrack(c).unlockAfterRead();
|
||||
}
|
||||
Util.Pair<Iterable<Candidate>, Iterable<Candidate>> f = finder.getCandidateDeltas(c, fixes);
|
||||
chooser.calculateMarkPassDeltas(c, f.getA(), f.getB());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void createXML() throws IOException {
|
||||
double accuracy = correct / totalPasses;
|
||||
|
||||
+6
-4
@@ -17,6 +17,7 @@ import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.Sideline;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.impl.BoatClassImpl;
|
||||
import com.sap.sailing.domain.base.impl.BoatImpl;
|
||||
import com.sap.sailing.domain.base.impl.CompetitorImpl;
|
||||
import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl;
|
||||
import com.sap.sailing.domain.base.impl.CourseAreaImpl;
|
||||
@@ -30,6 +31,7 @@ import com.sap.sailing.domain.base.impl.WaypointImpl;
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
|
||||
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
|
||||
@@ -43,9 +45,10 @@ import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class AbstractMockedRaceMarkPassingTest {
|
||||
protected Competitor ron = new CompetitorImpl("Ron", "Ron", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null);
|
||||
protected Competitor tom = new CompetitorImpl("Tom", "Tom", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null);
|
||||
protected Competitor ben = new CompetitorImpl("Ben", "Ben", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null);
|
||||
final BoatClassImpl boatClass = new BoatClassImpl("boat", true, "boat", new MeterDistance(10), new MeterDistance(5), null);
|
||||
protected Competitor ron = new CompetitorImpl("Ron", "Ron", null, null, null, null, new BoatImpl("boat", boatClass, "boot"), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null);
|
||||
protected Competitor tom = new CompetitorImpl("Tom", "Tom", null, null, null, null, new BoatImpl("boat", boatClass, "boot"), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null);
|
||||
protected Competitor ben = new CompetitorImpl("Ben", "Ben", null, null, null, null, new BoatImpl("boat", boatClass, "boot"), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null);
|
||||
|
||||
protected Mark m = new MarkImpl("Mark");
|
||||
protected Mark gate1 = new MarkImpl("Gate1");
|
||||
@@ -64,7 +67,6 @@ public class AbstractMockedRaceMarkPassingTest {
|
||||
Waypoint w4 = new WaypointImpl(m, PassingInstruction.Starboard);
|
||||
Waypoint w5 = new WaypointImpl(cp, PassingInstruction.Line);
|
||||
waypoints = Arrays.asList(w1, w2, w3, w4, w5);
|
||||
final BoatClassImpl boatClass = new BoatClassImpl("boat", true);
|
||||
Regatta r = new RegattaImpl(RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass,
|
||||
/*startDate*/ null, /*endDate*/ null, Arrays.asList(new SeriesImpl("Series", true, Arrays.asList(new FleetImpl("fleet")),
|
||||
new ArrayList<String>(), null)), true, new HighPoint(), "ID", new CourseAreaImpl("area", new UUID(5, 5)), OneDesignRankingMetric::new);
|
||||
|
||||
-1
@@ -153,7 +153,6 @@ public class FlorianopolisMarkPassingTest extends AbstractMarkPassingTest {
|
||||
*
|
||||
* @Test public void testRace26() throws IOException, InterruptedException, URISyntaxException { testRace(26); }
|
||||
*/
|
||||
|
||||
@Override
|
||||
protected String getExpectedEventName() {
|
||||
return "ESS Florianopolis 2013";
|
||||
|
||||
+19
-3
@@ -7,17 +7,33 @@ import java.text.ParseException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartProcedureChangedEventImpl;
|
||||
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.tractrac.model.lib.api.event.CreateModelException;
|
||||
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
|
||||
|
||||
public class Kiel2013505Test extends AbstractMarkPassingTest {
|
||||
|
||||
public Kiel2013505Test() throws MalformedURLException, URISyntaxException {
|
||||
public Kiel2013505Test() throws MalformedURLException, URISyntaxException {
|
||||
super();
|
||||
}
|
||||
|
||||
//Takes longer than 2 minutes, so the rule in AbstractTracTracLiveTest needs to be deactivated first.
|
||||
|
||||
/**
|
||||
* Marks the race as using a gate start
|
||||
*/
|
||||
@Override
|
||||
protected void setUp(String raceNumber) throws IOException, InterruptedException, URISyntaxException,
|
||||
ParseException, SubscriberInitializationException, CreateModelException {
|
||||
super.setUp(raceNumber);
|
||||
final RaceLog raceLog = new RaceLogImpl("505 Race Log");
|
||||
getTrackedRace().attachRaceLog(raceLog);
|
||||
raceLog.add(new RaceLogStartProcedureChangedEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 0, RacingProcedureType.GateStart));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRace5() throws IOException, InterruptedException, URISyntaxException, ParseException, SubscriberInitializationException, CreateModelException {
|
||||
testRace("5");
|
||||
|
||||
+6
-6
@@ -45,7 +45,7 @@ public class MarkPassingCalculatorPerformanceTest extends AbstractMockedRaceMark
|
||||
|
||||
@AfterClass
|
||||
public static void createXMLFile() throws IOException {
|
||||
MeasurementXMLFile performanceReport = new MeasurementXMLFile(MarkPassingCalculatorPerformanceTest.class);
|
||||
final MeasurementXMLFile performanceReport = new MeasurementXMLFile(MarkPassingCalculatorPerformanceTest.class);
|
||||
for (String key : result.keySet()) {
|
||||
MeasurementCase performanceReportCase = performanceReport.addCase(key);
|
||||
performanceReportCase.addMeasurement(new Measurement(key, result.get(key)));
|
||||
@@ -72,7 +72,7 @@ public class MarkPassingCalculatorPerformanceTest extends AbstractMockedRaceMark
|
||||
|
||||
@Test
|
||||
public void testChooser() {
|
||||
long time = timeToAddCandidatesToChooser(500, 1, 25);
|
||||
final long time = timeToAddCandidatesToChooser(500, 1, 25);
|
||||
System.out.println(time);
|
||||
result.put("ChooserPerformance", time);
|
||||
assertTrue(time < 5000);
|
||||
@@ -96,16 +96,16 @@ public class MarkPassingCalculatorPerformanceTest extends AbstractMockedRaceMark
|
||||
}
|
||||
|
||||
private CandidateImpl randomCan() {
|
||||
int id = rnd.nextInt(3);
|
||||
final int id = rnd.nextInt(3);
|
||||
return new CandidateImpl(id + 1, new MillisecondsTimePoint((long) (rnd.nextDouble() * 200000)),
|
||||
0.5 + 0.5 * rnd.nextDouble(), Util.get(race.getRace().getCourse().getWaypoints(), id));
|
||||
}
|
||||
|
||||
private GPSFixMoving rndFix() {
|
||||
DegreePosition position = new DegreePosition(0.001 - rnd.nextDouble() * 0.001,
|
||||
final DegreePosition position = new DegreePosition(0.001 - rnd.nextDouble() * 0.001,
|
||||
0.0002 - rnd.nextDouble() * 0.0004);
|
||||
TimePoint p = new MillisecondsTimePoint((long) (rnd.nextDouble() * 200000));
|
||||
SpeedWithBearing speed = new KnotSpeedWithBearingImpl(rnd.nextInt(11), new DegreeBearingImpl(rnd.nextInt(360)));
|
||||
final TimePoint p = new MillisecondsTimePoint((long) (rnd.nextDouble() * 200000));
|
||||
final SpeedWithBearing speed = new KnotSpeedWithBearingImpl(rnd.nextInt(11), new DegreeBearingImpl(rnd.nextInt(360)));
|
||||
return new GPSFixMovingImpl(position, p, speed);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@ import java.util.List;
|
||||
import java.util.NavigableSet;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.Ignore;
|
||||
|
||||
import com.sap.sailing.domain.base.Course;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
@@ -34,6 +35,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
* Tests tricky situation that can fail easily. Created with help of http://itouchmap.com/latlong.html
|
||||
*
|
||||
*/
|
||||
@Ignore
|
||||
public class MarkPassingWhiteBoxTest extends AbstractMockedRaceMarkPassingTest {
|
||||
|
||||
@Test
|
||||
@@ -78,7 +80,6 @@ public class MarkPassingWhiteBoxTest extends AbstractMockedRaceMarkPassingTest {
|
||||
chooser.suppressMarkPassings(ron, 1);
|
||||
NavigableSet<MarkPassing> markPassingsAfterSupressing = race.getMarkPassings(ron);
|
||||
assertEquals(0, markPassingsAfterSupressing.size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+95
-2
@@ -1,12 +1,31 @@
|
||||
package com.sap.sailing.domain.test.markpassing;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.markpassingcalculation.CandidateChooser;
|
||||
import com.sap.sailing.domain.markpassingcalculation.CandidateFinder;
|
||||
import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator;
|
||||
import com.sap.sailing.domain.markpassingcalculation.impl.CandidateChooserImpl;
|
||||
import com.sap.sailing.domain.markpassingcalculation.impl.CandidateFinderImpl;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sse.InvalidDateException;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.util.DateParser;
|
||||
import com.tractrac.model.lib.api.event.CreateModelException;
|
||||
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
|
||||
|
||||
@@ -18,8 +37,82 @@ public class StarIDM2013MarkPassingTest extends AbstractMarkPassingTest {
|
||||
|
||||
@Test
|
||||
public void testRace5() throws IOException, InterruptedException, URISyntaxException, ParseException,
|
||||
SubscriberInitializationException, CreateModelException {
|
||||
testRace("5");
|
||||
SubscriberInitializationException, CreateModelException, InvalidDateException {
|
||||
// Race 5 is really broken on TracTrac's side. I've tried re-calculating the mark passing to no avail.
|
||||
// Now it's even worse, and only the windward mark passings and the start mark passings are recognized
|
||||
// half-way reasonably, but no mark passings seem to have been detected for subsequent waypoints.
|
||||
// To still use this race for testing purposes, let's do spot checks based on manually identified mark
|
||||
// passings, based on https://www.sapsailing.com/gwt/RaceBoard.html?eventId=a43b8f96-1e77-448a-9b8a-0f586f1bb18f&leaderboardName=Star%20IDM%202013&leaderboardGroupName=Star%20IDM%202013&raceName=Race%205&viewShowMapControls=true&viewShowNavigationPanel=true®attaName=IDM%20Starboot%202013%20%28Star%29
|
||||
setUp("5");
|
||||
synchronized (getSemaphor()) {
|
||||
while (!isStoredDataLoaded()) {
|
||||
getSemaphor().wait();
|
||||
}
|
||||
}
|
||||
testWholeRace5();
|
||||
testOnlyStartOfRace5();
|
||||
}
|
||||
|
||||
private void testWholeRace5() throws InvalidDateException {
|
||||
new MarkPassingCalculator(getTrackedRace(), false); // do our calculation, injecting into TrackedRace
|
||||
final Competitor GER7897 = getCompetitorByName("Tusch, Frank,Winkelmann, Sven");
|
||||
assertMarkPassing(GER7897, Util.get(getWaypoints(), 0), DateParser.parse("2013-05-04T14:50:10+0200"));
|
||||
assertMarkPassing(GER7897, Util.get(getWaypoints(), 1), DateParser.parse("2013-05-04T14:57:12+0200"));
|
||||
assertMarkPassing(GER7897, Util.get(getWaypoints(), 3), DateParser.parse("2013-05-04T15:06:32+0200"));
|
||||
assertMarkPassing(GER7897, Util.get(getWaypoints(), 4), DateParser.parse("2013-05-04T15:15:01+0200"));
|
||||
assertMarkPassing(GER7897, Util.get(getWaypoints(), 6), DateParser.parse("2013-05-04T15:21:17+0200"));
|
||||
assertMarkPassing(GER7897, Util.get(getWaypoints(), 7), DateParser.parse("2013-05-04T15:29:18+0200"));
|
||||
assertMarkPassing(GER7897, Util.get(getWaypoints(), 9), DateParser.parse("2013-05-04T15:36:36+0200"));
|
||||
assertMarkPassing(GER7897, Util.get(getWaypoints(), 10), DateParser.parse("2013-05-04T15:38:09+0200"));
|
||||
|
||||
final Competitor GER7616 = getCompetitorByName("Seefelder, Jürgen,Seefelder, N"); // starts late
|
||||
assertMarkPassing(GER7616, Util.get(getWaypoints(), 0), DateParser.parse("2013-05-04T14:50:57+0200"));
|
||||
assertMarkPassing(GER7616, Util.get(getWaypoints(), 1), DateParser.parse("2013-05-04T14:59:05+0200"));
|
||||
assertMarkPassing(GER7616, Util.get(getWaypoints(), 3), DateParser.parse("2013-05-04T15:07:32+0200"));
|
||||
assertMarkPassing(GER7616, Util.get(getWaypoints(), 4), DateParser.parse("2013-05-04T15:16:40+0200"));
|
||||
assertMarkPassing(GER7616, Util.get(getWaypoints(), 6), DateParser.parse("2013-05-04T15:23:26+0200"));
|
||||
assertMarkPassing(GER7616, Util.get(getWaypoints(), 7), DateParser.parse("2013-05-04T15:33:01+0200"));
|
||||
assertMarkPassing(GER7616, Util.get(getWaypoints(), 9), DateParser.parse("2013-05-04T15:39:25+0200"));
|
||||
assertMarkPassing(GER7616, Util.get(getWaypoints(), 10), DateParser.parse("2013-05-04T15:40:51+0200"));
|
||||
|
||||
final Competitor GER8340 = getCompetitorByName("Griese, Achim,Marcour, Michael"); // starts rather early
|
||||
assertMarkPassing(GER8340, Util.get(getWaypoints(), 0), DateParser.parse("2013-05-04T14:50:04+0200"));
|
||||
assertMarkPassing(GER8340, Util.get(getWaypoints(), 1), DateParser.parse("2013-05-04T14:56:57+0200"));
|
||||
assertMarkPassing(GER8340, Util.get(getWaypoints(), 3), DateParser.parse("2013-05-04T15:06:33+0200"));
|
||||
assertMarkPassing(GER8340, Util.get(getWaypoints(), 4), DateParser.parse("2013-05-04T15:14:31+0200"));
|
||||
assertMarkPassing(GER8340, Util.get(getWaypoints(), 6), DateParser.parse("2013-05-04T15:20:29+0200"));
|
||||
assertMarkPassing(GER8340, Util.get(getWaypoints(), 7), DateParser.parse("2013-05-04T15:28:34+0200"));
|
||||
assertMarkPassing(GER8340, Util.get(getWaypoints(), 9), DateParser.parse("2013-05-04T15:35:52+0200"));
|
||||
assertMarkPassing(GER8340, Util.get(getWaypoints(), 10), DateParser.parse("2013-05-04T15:37:22+0200"));
|
||||
}
|
||||
|
||||
private void testOnlyStartOfRace5() throws InvalidDateException {
|
||||
// clear mark passings
|
||||
for (Competitor c : getRace().getCompetitors()) {
|
||||
getTrackedRace().updateMarkPassings(c, Collections.emptySet());
|
||||
}
|
||||
CandidateFinder finder = new CandidateFinderImpl(getTrackedRace());
|
||||
CandidateChooser chooser = new CandidateChooserImpl(getTrackedRace());
|
||||
final TimePoint afterStart = new MillisecondsTimePoint(DateParser.parse("2013-05-04T14:52:30+0200"));
|
||||
for (Competitor c : getRace().getCompetitors()) {
|
||||
calculateMarkPassingsForPartialTrack(c, afterStart, finder, chooser);
|
||||
}
|
||||
final Competitor GER7897 = getCompetitorByName("Tusch, Frank,Winkelmann, Sven");
|
||||
assertMarkPassing(GER7897, Util.get(getWaypoints(), 0), DateParser.parse("2013-05-04T14:50:10+0200"));
|
||||
final Competitor GER7616 = getCompetitorByName("Seefelder, Jürgen,Seefelder, N"); // starts late
|
||||
assertMarkPassing(GER7616, Util.get(getWaypoints(), 0), DateParser.parse("2013-05-04T14:50:57+0200"));
|
||||
final Competitor GER8340 = getCompetitorByName("Griese, Achim,Marcour, Michael"); // starts rather early
|
||||
assertMarkPassing(GER8340, Util.get(getWaypoints(), 0), DateParser.parse("2013-05-04T14:50:04+0200"));
|
||||
}
|
||||
|
||||
private void assertMarkPassing(Competitor competitor, Waypoint waypoint, Date when) {
|
||||
final MarkPassing markPassing = getTrackedRace().getMarkPassing(competitor, waypoint);
|
||||
if (markPassing == null) {
|
||||
fail("Expected mark passing for "+competitor+" and "+waypoint+" around "+when+" but no mark passing found");
|
||||
}
|
||||
final Duration offset = new MillisecondsTimePoint(when).until(markPassing.getTimePoint()).abs();
|
||||
assertTrue("Expected mark passing for "+competitor+" and "+waypoint+" around "+when+
|
||||
" but found one at "+markPassing.getTimePoint()+" which is "+offset+" off.", offset.asSeconds() < 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+5
@@ -100,6 +100,11 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getStartOfRace(boolean inferred) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<TrackedLeg> getTrackedLegs() {
|
||||
return null;
|
||||
|
||||
+5
@@ -111,6 +111,11 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getStartOfRace(boolean inferred) {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getEndOfRace() {
|
||||
return null;
|
||||
|
||||
@@ -792,6 +792,6 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
|
||||
public void deregisterCompetitors(Iterable<Competitor> competitors) {
|
||||
RegattaLog regattaLog = getRegattaLike().getRegattaLog();
|
||||
CompetitorDeregistrator<RegattaLog, RegattaLogEvent, RegattaLogEventVisitor> deregisterer = new CompetitorDeregistrator<>(regattaLog, competitors, regattaLogEventAuthorForRegatta);
|
||||
deregisterer.analyze();
|
||||
deregisterer.deregister(deregisterer.analyze());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -224,6 +224,6 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI
|
||||
public void deregisterCompetitors(Iterable<Competitor> competitors) {
|
||||
RegattaLog regattaLog = getRegattaLike().getRegattaLog();
|
||||
CompetitorDeregistrator<RegattaLog, RegattaLogEvent, RegattaLogEventVisitor> deregisterer = new CompetitorDeregistrator<>(regattaLog, competitors, regattaLogEventAuthorForAbstractLeaderboard);
|
||||
deregisterer.analyze();
|
||||
deregisterer.deregister(deregisterer.analyze());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -231,8 +231,7 @@ public class MarkPassingCalculator {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.finer("Calculating MarkPassings for " + c + " (" + Util.size(fixes) + " new fixes)");
|
||||
Util.Pair<Iterable<Candidate>, Iterable<Candidate>> candidateDeltas = finder.getCandidateDeltas(c,
|
||||
fixes);
|
||||
Util.Pair<Iterable<Candidate>, Iterable<Candidate>> candidateDeltas = finder.getCandidateDeltas(c, fixes);
|
||||
logger.finer("Received " + Util.size(candidateDeltas.getA()) + " new Candidates and will remove "
|
||||
+ Util.size(candidateDeltas.getB()) + " old Candidates for " + c);
|
||||
chooser.calculateMarkPassDeltas(c, candidateDeltas.getA(), candidateDeltas.getB());
|
||||
|
||||
+81
-47
@@ -25,10 +25,12 @@ import com.sap.sailing.domain.markpassingcalculation.CandidateChooser;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsDurationImpl;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
/**
|
||||
@@ -44,9 +46,28 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
*
|
||||
*/
|
||||
public class CandidateChooserImpl implements CandidateChooser {
|
||||
// TODO what is the meaning of this constant?
|
||||
private static final int MILLISECONDS_BEFORE_STARTTIME = 5000;
|
||||
private final static double MINIMUM_PROBABILITY = 1 - Edge.getPenaltyForSkipping();
|
||||
/**
|
||||
* Distance ratios of actual distance traveled and leg length above this threshold will receive
|
||||
* penalties on their probability. Ratios below 1.0 receive the ratio as the penalty.
|
||||
* See {@link #getDistanceEstimationBasedProbability(Competitor, Candidate, Candidate)}.
|
||||
*/
|
||||
final double MAX_REASONABLE_RATIO_BETWEEN_DISTANCE_TRAVELED_AND_LEG_LENGTH = 2.0;
|
||||
|
||||
/**
|
||||
* Start mark passings will be considered this much before the actual race start. The race start
|
||||
* as identified by {@link TrackedRace#getStartOfRace()} is therefore {@link #EARLY_STARTS_CONSIDERED_THIS_MUCH_BEFORE_STARTTIME}
|
||||
* after {@link #raceStartTime}.
|
||||
*/
|
||||
private static final Duration EARLY_STARTS_CONSIDERED_THIS_MUCH_BEFORE_STARTTIME = new MillisecondsDurationImpl(5000);
|
||||
|
||||
/**
|
||||
* The duration after which a start mark passing's probability is considered only 50%. A perfect start mark
|
||||
* passing happening exactly at the race start time has time-wise probability of 1.0. Another delay of this much
|
||||
* lets the probability drop to 1/3, and so on.
|
||||
*/
|
||||
private static final Duration DELAY_AFTER_WHICH_PROBABILITY_OF_START_HALVES = Duration.ONE_MINUTE;
|
||||
|
||||
private final static double MINIMUM_PROBABILITY = Edge.getPenaltyForSkipping();
|
||||
|
||||
private static final Logger logger = Logger.getLogger(CandidateChooserImpl.class.getName());
|
||||
|
||||
@@ -55,14 +76,20 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
private Map<Competitor, Set<Candidate>> candidates = new HashMap<>();
|
||||
private Map<Competitor, NavigableSet<Candidate>> fixedPassings = new HashMap<>();
|
||||
private Map<Competitor, Integer> suppressedPassings = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Set to {@link #EARLY_STARTS_CONSIDERED_THIS_MUCH_BEFORE_STARTTIME} milliseconds before the actual race start,
|
||||
* in case the actual start of race is known; {@code null} otherwise.
|
||||
*/
|
||||
private TimePoint raceStartTime;
|
||||
private final WaypointPositionAndDistanceCache waypointPositionAndDistanceCache;
|
||||
|
||||
/**
|
||||
* An artificial proxy candidate that comes before the start mark passing. Its time point is set to
|
||||
* {@link #MILLISECONDS_BEFORE_STARTTIME} milliseconds before the race start time or <code>null</code>
|
||||
* {@link #EARLY_STARTS_CONSIDERED_THIS_MUCH_BEFORE_STARTTIME} milliseconds before the race start time or <code>null</code>
|
||||
* in case the race start time is not known.
|
||||
*/
|
||||
|
||||
private final CandidateWithSettableTime start;
|
||||
private final CandidateWithSettableWaypointIndex end;
|
||||
private final DynamicTrackedRace race;
|
||||
@@ -70,7 +97,9 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
public CandidateChooserImpl(DynamicTrackedRace race) {
|
||||
this.race = race;
|
||||
waypointPositionAndDistanceCache = new WaypointPositionAndDistanceCache(race, Duration.ONE_MINUTE);
|
||||
raceStartTime = race.getStartOfRace() != null ? race.getStartOfRace().minus(MILLISECONDS_BEFORE_STARTTIME) : null;
|
||||
final TimePoint startOfRaceWithoutInference = race.getStartOfRace(/* inferred */ false);
|
||||
raceStartTime = startOfRaceWithoutInference != null ? startOfRaceWithoutInference.
|
||||
minus(EARLY_STARTS_CONSIDERED_THIS_MUCH_BEFORE_STARTTIME) : null;
|
||||
start = new CandidateWithSettableTime(/* Index */0, raceStartTime, /* Probability */1, /* Waypoint */null);
|
||||
end = new CandidateWithSettableWaypointIndex(race.getRace().getCourse().getNumberOfWaypoints() + 1, /* TimePoint */null,
|
||||
/* Probability */1, /* Waypoint */null);
|
||||
@@ -78,7 +107,16 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
List<Candidate> startAndEnd = Arrays.asList(start, end);
|
||||
for (Competitor c : race.getRace().getCompetitors()) {
|
||||
candidates.put(c, Collections.synchronizedSet(new TreeSet<Candidate>()));
|
||||
currentMarkPasses.put(c, new HashMap<Waypoint, MarkPassing>());
|
||||
final HashMap<Waypoint, MarkPassing> currentMarkPassesForCompetitor = new HashMap<Waypoint, MarkPassing>();
|
||||
currentMarkPasses.put(c, currentMarkPassesForCompetitor);
|
||||
// in case the tracked race already has mark passings, e.g., from another mark passing calculator,
|
||||
// ensure consistency of the currentMarkPasses map with the TrackedRace:
|
||||
for (final Waypoint w : race.getRace().getCourse().getWaypoints()) {
|
||||
final MarkPassing mp = race.getMarkPassing(c, w);
|
||||
if (mp != null) {
|
||||
currentMarkPassesForCompetitor.put(w, mp);
|
||||
}
|
||||
}
|
||||
TreeSet<Candidate> fixedPasses = new TreeSet<Candidate>(new Comparator<Candidate>() {
|
||||
@Override
|
||||
public int compare(Candidate o1, Candidate o2) {
|
||||
@@ -106,10 +144,10 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
|
||||
@Override
|
||||
public void calculateMarkPassDeltas(Competitor c, Iterable<Candidate> newCans, Iterable<Candidate> oldCans) {
|
||||
TimePoint startOfRace = race.getStartOfRace();
|
||||
final TimePoint startOfRace = race.getStartOfRace(/* inference */ false);
|
||||
if (startOfRace != null) {
|
||||
if (raceStartTime == null || !startOfRace.minus(MILLISECONDS_BEFORE_STARTTIME).equals(raceStartTime)) {
|
||||
raceStartTime = startOfRace.minus(MILLISECONDS_BEFORE_STARTTIME);
|
||||
if (raceStartTime == null || !startOfRace.minus(EARLY_STARTS_CONSIDERED_THIS_MUCH_BEFORE_STARTTIME).equals(raceStartTime)) {
|
||||
raceStartTime = startOfRace.minus(EARLY_STARTS_CONSIDERED_THIS_MUCH_BEFORE_STARTTIME);
|
||||
List<Candidate> startList = new ArrayList<>();
|
||||
startList.add(start);
|
||||
for (Competitor com : candidates.keySet()) {
|
||||
@@ -223,15 +261,15 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
// calculate the probability for the start being the start given its timing and multiply
|
||||
// with the estimation for the distance-based probability:
|
||||
final Duration timeGapBetweenStartOfRaceAndCandidateTimePoint = early.getTimePoint()
|
||||
.plus(MILLISECONDS_BEFORE_STARTTIME).until(late.getTimePoint());
|
||||
// Being MILLISECONDS_BEFORE_STARTTIME off means a probability of 1/2; being twice this time
|
||||
.plus(EARLY_STARTS_CONSIDERED_THIS_MUCH_BEFORE_STARTTIME).until(late.getTimePoint()).abs();
|
||||
// Being DELAY_AFTER_WHICH_PROBABILITY_OF_START_HALVES off means a probability of 1/2; being twice this time
|
||||
// off means 1/3, and so on
|
||||
startTimingProbability = (double) MILLISECONDS_BEFORE_STARTTIME
|
||||
/ (double) (MILLISECONDS_BEFORE_STARTTIME + Math
|
||||
.abs(timeGapBetweenStartOfRaceAndCandidateTimePoint.asMillis()));
|
||||
startTimingProbability = DELAY_AFTER_WHICH_PROBABILITY_OF_START_HALVES.divide(
|
||||
DELAY_AFTER_WHICH_PROBABILITY_OF_START_HALVES.plus(
|
||||
timeGapBetweenStartOfRaceAndCandidateTimePoint));
|
||||
estimatedDistanceProbability = 1;
|
||||
} else {
|
||||
startTimingProbability = 0.1; // can't really tell how well the start time was matched when
|
||||
startTimingProbability = 1; // can't really tell how well the start time was matched when
|
||||
// we don't have a start candidate
|
||||
estimatedDistanceProbability = late == end ? 1 : getDistanceEstimationBasedProbability(c, early, late);
|
||||
}
|
||||
@@ -295,40 +333,40 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
if (oneBasedIndexOfSuppressedWaypoint <= endOfFixedInterval.getOneBasedIndexOfWaypoint()) {
|
||||
endOfFixedInterval = end;
|
||||
}
|
||||
NavigableSet<Util.Pair<Edge, Double>> currentEdgesCheapestFirst = new TreeSet<>(new Comparator<Util.Pair<Edge, Double>>() {
|
||||
NavigableSet<Util.Pair<Edge, Double>> currentEdgesMoreLikelyFirst = new TreeSet<>(new Comparator<Util.Pair<Edge, Double>>() {
|
||||
@Override
|
||||
public int compare(Util.Pair<Edge, Double> o1, Util.Pair<Edge, Double> o2) {
|
||||
int result = o1.getB().compareTo(o2.getB());
|
||||
int result = o2.getB().compareTo(o1.getB());
|
||||
return result != 0 ? result : o1.getA().compareTo(o2.getA());
|
||||
}
|
||||
});
|
||||
Map<Candidate, Util.Pair<Candidate, Double>> candidateWithParentAndSmallestTotalCost = new HashMap<>();
|
||||
Map<Candidate, Util.Pair<Candidate, Double>> candidateWithParentAndHighestTotalProbability = new HashMap<>();
|
||||
int indexOfEndOfFixedInterval = endOfFixedInterval.getOneBasedIndexOfWaypoint();
|
||||
|
||||
boolean endFound = false;
|
||||
currentEdgesCheapestFirst.add(new Util.Pair<Edge, Double>(new Edge(new CandidateImpl(-1, null, /* estimated distance probability */ 1, null), startOfFixedInterval,
|
||||
0, race.getRace().getCourse().getNumberOfWaypoints()), 0.0));
|
||||
currentEdgesMoreLikelyFirst.add(new Util.Pair<Edge, Double>(new Edge(new CandidateImpl(-1, null, /* estimated distance probability */ 1, null), startOfFixedInterval,
|
||||
1, race.getRace().getCourse().getNumberOfWaypoints()), 1.0));
|
||||
while (!endFound) {
|
||||
Util.Pair<Edge, Double> cheapestEdgeWithCost = currentEdgesCheapestFirst.pollFirst();
|
||||
if (cheapestEdgeWithCost == null) {
|
||||
Util.Pair<Edge, Double> mostLikelyEdgeWithProbability = currentEdgesMoreLikelyFirst.pollFirst();
|
||||
if (mostLikelyEdgeWithProbability == null) {
|
||||
endFound = true;
|
||||
} else {
|
||||
Edge currentCheapestEdge = cheapestEdgeWithCost.getA();
|
||||
Double currentCheapestCost = cheapestEdgeWithCost.getB();
|
||||
Edge currentMostLikelyEdge = mostLikelyEdgeWithProbability.getA();
|
||||
Double currentHighestProbability = mostLikelyEdgeWithProbability.getB();
|
||||
// If the shortest path to this candidate is already known the new edge is not added.
|
||||
if (!candidateWithParentAndSmallestTotalCost.containsKey(currentCheapestEdge.getEnd())) {
|
||||
// The cheapest edge taking us to currentCheapestEdge.getEnd() is found. Remember it.
|
||||
candidateWithParentAndSmallestTotalCost.put(currentCheapestEdge.getEnd(), new Util.Pair<Candidate, Double>(
|
||||
currentCheapestEdge.getStart(), currentCheapestCost));
|
||||
if (!candidateWithParentAndHighestTotalProbability.containsKey(currentMostLikelyEdge.getEnd())) {
|
||||
// The most likely edge taking us to currentMostLikelyEdge.getEnd() is found. Remember it.
|
||||
candidateWithParentAndHighestTotalProbability.put(currentMostLikelyEdge.getEnd(), new Util.Pair<Candidate, Double>(
|
||||
currentMostLikelyEdge.getStart(), currentHighestProbability));
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.finest("Added "+ currentCheapestEdge + "as cheapest edge for " + c);
|
||||
logger.finest("Added "+ currentMostLikelyEdge + " as most likely edge for " + c);
|
||||
}
|
||||
endFound = currentCheapestEdge.getEnd() == endOfFixedInterval;
|
||||
endFound = currentMostLikelyEdge.getEnd() == endOfFixedInterval;
|
||||
if (!endFound) {
|
||||
// the end of the segment was not yet found; add edges leading away from
|
||||
// currentCheapestEdge.getEnd(), summing up their cost with the cost required
|
||||
// to reach currentCheapestEdge.getEnd()
|
||||
Set<Edge> edgesForNewCandidate = allCompetitorEdges.get(currentCheapestEdge.getEnd());
|
||||
// currentMostLikelyEdge.getEnd(), multiplying up their probabilities with the probability
|
||||
// of reaching currentMostLikelyEdge.getEnd()
|
||||
Set<Edge> edgesForNewCandidate = allCompetitorEdges.get(currentMostLikelyEdge.getEnd());
|
||||
if (edgesForNewCandidate != null) {
|
||||
for (Edge e : edgesForNewCandidate) {
|
||||
int oneBasedIndexOfEndOfEdge = e.getEnd().getOneBasedIndexOfWaypoint();
|
||||
@@ -336,7 +374,7 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
// the next fixed mark passing
|
||||
if (oneBasedIndexOfEndOfEdge <= indexOfEndOfFixedInterval
|
||||
&& (oneBasedIndexOfEndOfEdge < oneBasedIndexOfSuppressedWaypoint || e.getEnd() == end)) {
|
||||
currentEdgesCheapestFirst.add(new Util.Pair<Edge, Double>(e, currentCheapestCost + e.getCost()));
|
||||
currentEdgesMoreLikelyFirst.add(new Util.Pair<Edge, Double>(e, currentHighestProbability * e.getProbability()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -344,10 +382,10 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
}
|
||||
}
|
||||
}
|
||||
Candidate marker = candidateWithParentAndSmallestTotalCost.get(endOfFixedInterval).getA();
|
||||
Candidate marker = candidateWithParentAndHighestTotalProbability.get(endOfFixedInterval).getA();
|
||||
while (marker.getOneBasedIndexOfWaypoint() > 0) {
|
||||
mostLikelyCandidates.add(marker);
|
||||
marker = candidateWithParentAndSmallestTotalCost.get(marker).getA();
|
||||
marker = candidateWithParentAndHighestTotalProbability.get(marker).getA();
|
||||
}
|
||||
startOfFixedInterval = endOfFixedInterval;
|
||||
endOfFixedInterval = fixedPasses.higher(endOfFixedInterval);
|
||||
@@ -399,8 +437,8 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
first = c1.getWaypoint();
|
||||
}
|
||||
final Waypoint second = c2.getWaypoint();
|
||||
Distance totalGreatCircleDistance = getApproximateTotalGreatCircleDistanceBetweenWaypoints(first, second, middleOfc1Andc2);
|
||||
Distance actualDistanceTraveled = race.getTrack(c).getDistanceTraveled(c1.getTimePoint(), c2.getTimePoint());
|
||||
final Distance totalGreatCircleDistance = getApproximateTotalGreatCircleDistanceBetweenWaypoints(first, second, middleOfc1Andc2);
|
||||
final Distance actualDistanceTraveled = race.getTrack(c).getDistanceTraveled(c1.getTimePoint(), c2.getTimePoint());
|
||||
result = getProbabilityOfActualDistanceGivenGreatCircleDistance(totalGreatCircleDistance, actualDistanceTraveled);
|
||||
return result;
|
||||
}
|
||||
@@ -418,23 +456,19 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
* @return a number between 0 and 1 with 1 representing a "fair chance" that the actual distance sailed could have
|
||||
* been sailed for the given great circle distance; 1 is returned for actual distances being in the range of
|
||||
* 1..2 times the great circle distance. Actual distances outside this interval reduce probability linearly
|
||||
* for smaller distances (gradient 3.5) and varies with the square root for distances that exceed twice the
|
||||
* for smaller distances (gradient 0.5) and varies with the one over the ratio for distances that exceed twice the
|
||||
* great circle distance.
|
||||
*/
|
||||
private double getProbabilityOfActualDistanceGivenGreatCircleDistance(Distance totalGreatCircleDistance,
|
||||
Distance actualDistanceTraveled) {
|
||||
final double result;
|
||||
double differenceInMeters = actualDistanceTraveled.getMeters() - totalGreatCircleDistance.getMeters();
|
||||
double ratio = differenceInMeters / totalGreatCircleDistance.getMeters();
|
||||
final double ratio = actualDistanceTraveled.getMeters() / totalGreatCircleDistance.getMeters();
|
||||
// A smaller distance than great circle from mark to mark is very unlikely, somewhere between the distance
|
||||
// estimated and double that is likely and anything greater than that gradually becomes unlikely
|
||||
if (ratio < 0) {
|
||||
// TODO shouldn't these factors be constants in the class header for easy fine-tuning?
|
||||
result = 3.5 * ratio + 1;
|
||||
} else if (ratio > 1) {
|
||||
result = Math.sqrt((-ratio + 3) / 2);
|
||||
if (ratio <= MAX_REASONABLE_RATIO_BETWEEN_DISTANCE_TRAVELED_AND_LEG_LENGTH) {
|
||||
result = Math.min(1.0, ratio); // maximum probability is 1; use for the interval between 1 and MAX_REASONABLE_RATIO_BETWEEN_DISTANCE_TRAVELED_AND_LEG_LENGTH
|
||||
} else {
|
||||
result = 1;
|
||||
result = 1./(ratio-1.); // start at probability 1 for ratio==2
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+364
-60
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.markpassingcalculation.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@@ -35,6 +36,7 @@ import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.TimeRange;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
|
||||
/**
|
||||
* The standard implemantation of {@link CandidateFinder}. There are two kinds of {@link Candidate}s. First of all,
|
||||
@@ -49,22 +51,47 @@ import com.sap.sse.common.Util;
|
||||
public class CandidateFinderImpl implements CandidateFinder {
|
||||
|
||||
// The higher this is, the closer the fixes have to be to waypoint to become a Candidate
|
||||
private final int strictnessOfDistanceBasedProbability = 8;
|
||||
private final int STRICTNESS_OF_DISTANCE_BASED_PROBABILITY = 7;
|
||||
|
||||
// All of the penalties are multiplied onto the probability of a Candidate. A value of 0 excludes Candidates that do
|
||||
// not fit, a value of 1 imposes no penalty on each criteria
|
||||
private final double penaltyForWrongSide = 0.7;
|
||||
private final double penaltyForWrongDirection = 0.7;
|
||||
private final double penaltyForDistanceCandidates = 0.7;
|
||||
private static final double PENALTY_FOR_WRONG_SIDE = 0.8;
|
||||
|
||||
/**
|
||||
* The penalty factor multiplied with a candidate's probability in case the competitor passes the line of an XTE
|
||||
* candidate with passing instructions other than {@link PassingInstruction#Line} in the wrong direction, e.g.,
|
||||
* passing a single mark that is to be passed to port in the wrong direction.
|
||||
*/
|
||||
private static final double PENALTY_FOR_WRONG_DIRECTION = 0.7;
|
||||
|
||||
/**
|
||||
* The penalty factor multiplied with a candidate's probability in case the competitor passes an XTE candidate for a
|
||||
* {@link PassingInstruction#Line line} in the wrong direction, e.g., passing a start line the wrong way.
|
||||
*/
|
||||
private static final double PENALTY_FOR_LINE_PASSED_IN_WRONG_DIRECTION = 0.3;
|
||||
|
||||
/**
|
||||
* Normally, for each distance candidate there should also be a proper XTE candidate that also tells about the
|
||||
* direction in which the boat crosses the line. XTE candidates are more precise and get a penalty in case the competitor
|
||||
* crosses the virtual line in the wrong direction: {@link #PENALTY_FOR_WRONG_DIRECTION}. The penalty a competitor should
|
||||
* get for not triggering a proper XTE candidate and only producing a candidate by getting near a mark and then
|
||||
* moving away again should be penalized at least as badly as {@link #PENALTY_FOR_WRONG_DIRECTION}.
|
||||
*/
|
||||
private static final double PENALTY_FOR_DISTANCE_CANDIDATES = 0.9 * PENALTY_FOR_WRONG_DIRECTION;
|
||||
|
||||
private static final double WORST_PENALTY_FOR_OTHER_COMPETITORS_BEING_FAR_FROM_START = 0.1;
|
||||
private static final double NUMBER_OF_HULL_LENGTHS_DISTANCE_FROM_START_AT_WHICH_WORST_PENALTY_APPLIES = 10;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(CandidateFinderImpl.class.getName());
|
||||
|
||||
|
||||
private Map<Competitor, LinkedHashMap<GPSFix, Map<Waypoint, List<Distance>>>> distanceCache = new LinkedHashMap<>();
|
||||
private Map<Competitor, LinkedHashMap<GPSFix, Map<Waypoint, List<Distance>>>> xteCache = new LinkedHashMap<>();
|
||||
|
||||
private Map<Competitor, Map<Waypoint, Map<List<GPSFix>, Candidate>>> xteCandidates = new HashMap<>();
|
||||
private Map<Competitor, Map<Waypoint, Map<GPSFix, Candidate>>> distanceCandidates = new HashMap<>();
|
||||
private final DynamicTrackedRace race;
|
||||
private final double penaltyForSkipping = 1 - Edge.getPenaltyForSkipping();
|
||||
private final double penaltyForSkipping = Edge.getPenaltyForSkipping();
|
||||
private final Map<Waypoint, PassingInstruction> passingInstructions = new LinkedHashMap<>();
|
||||
private final Comparator<GPSFix> comp = new Comparator<GPSFix>() {
|
||||
@Override
|
||||
@@ -122,8 +149,7 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
TimePoint end = null;
|
||||
for (Entry<Mark, List<GPSFix>> fixes : markFixes.entrySet()) {
|
||||
for (GPSFix fix : fixes.getValue()) {
|
||||
TimeRange timePoints = race.getOrCreateTrack(fixes.getKey()).getEstimatedPositionTimePeriodAffectedBy(
|
||||
fix);
|
||||
TimeRange timePoints = race.getOrCreateTrack(fixes.getKey()).getEstimatedPositionTimePeriodAffectedBy(fix);
|
||||
TimePoint newStart = timePoints.from();
|
||||
TimePoint newEnd = timePoints.to();
|
||||
start = start == null || start.after(newStart) ? newStart : start;
|
||||
@@ -131,24 +157,28 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
}
|
||||
}
|
||||
for (Competitor c : race.getRace().getCompetitors()) {
|
||||
List<GPSFix> comFixes = new ArrayList<>();
|
||||
List<GPSFix> competitorFixes = new ArrayList<>();
|
||||
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = race.getTrack(c);
|
||||
GPSFix comFix = track.getFirstFixAtOrAfter(start);
|
||||
if (comFix != null) {
|
||||
if (end != null) {
|
||||
while (comFix != null && !comFix.getTimePoint().after(end)) {
|
||||
comFixes.add(comFix);
|
||||
competitorFixes.add(comFix);
|
||||
distanceCache.get(c).remove(comFix);
|
||||
xteCache.get(c).remove(comFix);
|
||||
comFix = track.getFirstFixAfter(comFix.getTimePoint());
|
||||
}
|
||||
} else {
|
||||
while (comFix != null) {
|
||||
comFixes.add(comFix);
|
||||
competitorFixes.add(comFix);
|
||||
distanceCache.get(c).remove(comFix);
|
||||
xteCache.get(c).remove(comFix);
|
||||
comFix = track.getFirstFixAfter(comFix.getTimePoint());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!comFixes.isEmpty()) {
|
||||
affectedFixes.put(c, comFixes);
|
||||
if (!competitorFixes.isEmpty()) {
|
||||
affectedFixes.put(c, competitorFixes);
|
||||
}
|
||||
}
|
||||
return affectedFixes;
|
||||
@@ -156,7 +186,6 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
|
||||
@Override
|
||||
public Util.Pair<Iterable<Candidate>, Iterable<Candidate>> getCandidateDeltas(Competitor c, Iterable<GPSFix> fixes) {
|
||||
|
||||
List<Candidate> newCans = new ArrayList<>();
|
||||
List<Candidate> wrongCans = new ArrayList<>();
|
||||
Course course = race.getRace().getCourse();
|
||||
@@ -293,7 +322,7 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
if (numberofMarks == 2) {
|
||||
instruction = PassingInstruction.Gate;
|
||||
} else if (numberofMarks == 1) {
|
||||
instruction = PassingInstruction.Port;
|
||||
instruction = PassingInstruction.Single_Unknown;
|
||||
} else {
|
||||
instruction = PassingInstruction.None;
|
||||
}
|
||||
@@ -370,6 +399,10 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
Boolean isCan = false;
|
||||
Candidate oldCan = null;
|
||||
Double probability = null;
|
||||
double sidePenalty = 1;
|
||||
Distance distance = null;
|
||||
Double startProbabilityBasedOnOtherCompetitors = null;
|
||||
double onCorrectSideOfWaypoint = 0.8;
|
||||
List<Distance> waypointDistances = fixDistances.get(w);
|
||||
List<Distance> waypointDistancesBefore = fixDistancesBefore.get(w);
|
||||
List<Distance> waypointDistancesAfter = fixDistancesAfter.get(w);
|
||||
@@ -394,12 +427,26 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
p = fix.getPosition();
|
||||
Double newProbability = getDistanceBasedProbability(w, t, dis);
|
||||
if (newProbability != null) {
|
||||
newProbability *= isOnCorrectSideOfWaypoint(w, p, t, portMark) ? penaltyForDistanceCandidates
|
||||
: penaltyForDistanceCandidates * penaltyForWrongSide;
|
||||
// FIXME why not generate the candidate here where we have all information at hand?
|
||||
final double newOnCorrectSideOfWaypointPenalty = getSidePenalty(w, p, t, portMark);
|
||||
newProbability *= newOnCorrectSideOfWaypointPenalty * PENALTY_FOR_DISTANCE_CANDIDATES;
|
||||
final Double newStartProbabilityBasedOnOtherCompetitors;
|
||||
if (isStartWaypoint(w)) {
|
||||
newStartProbabilityBasedOnOtherCompetitors = getProbabilityForStartCandidateBasedOnOtherCompetitorsBehavior(c, t);
|
||||
if (newStartProbabilityBasedOnOtherCompetitors != null) {
|
||||
newProbability *= newStartProbabilityBasedOnOtherCompetitors;
|
||||
}
|
||||
} else {
|
||||
newStartProbabilityBasedOnOtherCompetitors = null;
|
||||
}
|
||||
if (newProbability > penaltyForSkipping
|
||||
&& (probability == null || newProbability > probability)) {
|
||||
isCan = true;
|
||||
probability = newProbability;
|
||||
sidePenalty = newOnCorrectSideOfWaypointPenalty;
|
||||
onCorrectSideOfWaypoint = newOnCorrectSideOfWaypointPenalty;
|
||||
distance = dis;
|
||||
startProbabilityBasedOnOtherCompetitors = newStartProbabilityBasedOnOtherCompetitors;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -411,8 +458,8 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
wasCan = true;
|
||||
}
|
||||
if (!wasCan && isCan) {
|
||||
Candidate newCan = new CandidateImpl(race.getRace().getCourse().getIndexOfWaypoint(w) + 1,
|
||||
t, probability, w);
|
||||
Candidate newCan = new DistanceCandidateImpl(race.getRace().getCourse().getIndexOfWaypoint(w) + 1,
|
||||
t, probability, startProbabilityBasedOnOtherCompetitors, w, sidePenalty, distance);
|
||||
getDistanceCandidates(c, w).put(fix, newCan);
|
||||
result.getA().add(newCan);
|
||||
logger.finest("Added distance" + newCan.toString() + "for " + c);
|
||||
@@ -420,8 +467,8 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
getDistanceCandidates(c, w).remove(fix);
|
||||
result.getB().add(oldCan);
|
||||
} else if (wasCan && isCan && oldCan.getProbability() != probability) {
|
||||
Candidate newCan = new CandidateImpl(race.getRace().getCourse().getIndexOfWaypoint(w) + 1,
|
||||
t, probability, w);
|
||||
Candidate newCan = new DistanceCandidateImpl(race.getRace().getCourse().getIndexOfWaypoint(w) + 1,
|
||||
t, probability, startProbabilityBasedOnOtherCompetitors, w, onCorrectSideOfWaypoint, distance);
|
||||
getDistanceCandidates(c, w).put(fix, newCan);
|
||||
result.getA().add(newCan);
|
||||
logger.finest("Added distance" + newCan.toString() + "for " + c);
|
||||
@@ -615,18 +662,245 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
* crossingInfo.getB()).getMeters()); } } } }
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@code xte1} and {@code xte2} are expected to have different signums or both be 0. The method
|
||||
* interpolates between the time points {@code t1} and {@code t2} according to where the XTE is
|
||||
* assumed to have crossed 0. The candidate is then generated for this interpolated time point.
|
||||
*/
|
||||
private Candidate createCandidate(Competitor c, double xte1, double xte2, TimePoint t1, TimePoint t2, Waypoint w,
|
||||
Boolean portMark) {
|
||||
long differenceInMillis = t2.asMillis() - t1.asMillis();
|
||||
double ratio = (Math.abs(xte1) / (Math.abs(xte1) + Math.abs(xte2)));
|
||||
TimePoint t = t1.plus((long) (differenceInMillis * ratio));
|
||||
Position p = race.getTrack(c).getEstimatedPosition(t, false);
|
||||
List<Distance> distances = calculateDistance(p, w, t);
|
||||
Distance d = portMark ? distances.get(0) : distances.get(1);
|
||||
double cost = getDistanceBasedProbability(w, t, d);
|
||||
cost = isOnCorrectSideOfWaypoint(w, p, t, portMark) ? cost : cost * penaltyForWrongSide;
|
||||
cost = passesInTheRightDirection(w, xte1, xte2, portMark) ? cost : cost * penaltyForWrongDirection;
|
||||
return new CandidateImpl(race.getRace().getCourse().getIndexOfWaypoint(w) + 1, t, cost, w);
|
||||
final long differenceInMillis = t2.asMillis() - t1.asMillis();
|
||||
final double ratio = (Math.abs(xte1) / (Math.abs(xte1) + Math.abs(xte2)));
|
||||
final TimePoint t = t1.plus((long) (differenceInMillis * ratio));
|
||||
final Position p = race.getTrack(c).getEstimatedPosition(t, false);
|
||||
final List<Distance> distances = calculateDistance(p, w, t);
|
||||
final Distance d = portMark ? distances.get(0) : distances.get(1);
|
||||
final double sidePenalty = getSidePenalty(w, p, t, portMark);
|
||||
double probability = getDistanceBasedProbability(w, t, d) * sidePenalty;
|
||||
final Double passesInTheRightDirectionProbability = passesInTheRightDirection(w, xte1, xte2, portMark);
|
||||
// null would mean "unknown"; no penalty for those cases
|
||||
probability = passesInTheRightDirectionProbability == null ? probability : probability * passesInTheRightDirectionProbability;
|
||||
final Double startProbabilityBasedOnOtherCompetitors;
|
||||
if (isStartWaypoint(w)) {
|
||||
// add a penalty for a start candidate if we don't know the start time, it's not a gate start and
|
||||
// at time point t the other competitors are largely not even close to the start waypoint;
|
||||
// this will make start candidates much more likely if many other competitors are also very close to the
|
||||
// start, and it will help ruling out those candidates where someone is practicing a start just for themselves
|
||||
startProbabilityBasedOnOtherCompetitors = getProbabilityForStartCandidateBasedOnOtherCompetitorsBehavior(c, t);
|
||||
if (startProbabilityBasedOnOtherCompetitors != null) {
|
||||
probability *= startProbabilityBasedOnOtherCompetitors;
|
||||
}
|
||||
} else {
|
||||
startProbabilityBasedOnOtherCompetitors = null;
|
||||
}
|
||||
return new XTECandidateImpl(race.getRace().getCourse().getIndexOfWaypoint(w) + 1, t, probability,
|
||||
startProbabilityBasedOnOtherCompetitors, w, sidePenalty, passesInTheRightDirectionProbability);
|
||||
}
|
||||
|
||||
public static class AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine {
|
||||
private final Distance absoluteGeometricDistance;
|
||||
private final Distance signedProjectedDistance;
|
||||
public AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine(Distance absoluteGeometricDistance,
|
||||
Distance signedProjectedDistance) {
|
||||
super();
|
||||
this.absoluteGeometricDistance = absoluteGeometricDistance;
|
||||
this.signedProjectedDistance = signedProjectedDistance;
|
||||
}
|
||||
public Distance getAbsoluteGeometricDistance() {
|
||||
return absoluteGeometricDistance;
|
||||
}
|
||||
public Distance getSignedProjectedDistance() {
|
||||
return signedProjectedDistance;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine [absoluteGeometricDistance="
|
||||
+ absoluteGeometricDistance + ", signedProjectedDistance=" + signedProjectedDistance + "]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For a fixed line start it is very likely that all competitors pass the start waypoint at about the same time, or
|
||||
* that only a few competitors lag behind, e.g., because of start time penalties. This requires everyone to at least
|
||||
* be pretty close to the start waypoint at the start time, of if we're looking at a competitor starting late, the
|
||||
* field would at least have little variance and be consistently on the course side of the start line.
|
||||
* <p>
|
||||
*
|
||||
* Therefore, a start mark passing candidate is very likely when a large share of competitors is very close to the
|
||||
* start line. If the other competitors are not very close to the start line, a start may still be likely if the
|
||||
* others are on the course side and have similar distance to the start line, whereas it becomes pretty unlikely
|
||||
* when everyone is sailing around anywhere on the course or behind the line but not being in close proximity to the
|
||||
* line and not having similar distances to the start line while being on course side.
|
||||
*
|
||||
* <ul>
|
||||
* <li>check distance variance of other competitors; low variance could mean they started jointly</li>
|
||||
* <li>for start *line*, consider the side on which the other competitors are; don't rule out a late start if other
|
||||
* competitors have low distance variance and are on course side of the line</li>
|
||||
* <li>generally consider distance of other competitors to line</li>
|
||||
* <li>dampen "outliers" such as trackers forgotten in harbor or trackers not currently sending (80% of competitors
|
||||
* that look like they are starting seems like a good guess)</li>
|
||||
* </ul>
|
||||
*
|
||||
* Approach: compute signed distances to start line (positive meaning on course side). Sort by absolute distance and
|
||||
* compute a {@link #weight(double) weighted} average where the boats farthest away are considered less relevant than
|
||||
* the ones closer, leading to a natural outlier removal.<p>
|
||||
*
|
||||
* For the special case of a late starter, consider the weighted variance of the signed distance from the start line.
|
||||
* A low variance with a positive average (meaning on course side) indicates that everyone else may have started around the
|
||||
* same time, making this a probable late starter's candidate. Therefore, low variance with positive average shall increase
|
||||
* a probability that was low because of a large weighted average distance.
|
||||
*
|
||||
* @param t
|
||||
* the time point at which to consider the other competitors behavior relative to the start waypoint
|
||||
* @return {@code null} if the {@link #race} has a gate start or the start time is known; a probability between 0..1
|
||||
* (inclusive) where 0 means that based on all but {@code c}'s relation to the start line it seems
|
||||
* completely unlikely that a candidate for {@code c} at time {@code t} could have been a start candidate; 1
|
||||
* meaning that based on the other competitors' relation to the start line at time point {@code t} is seems
|
||||
* a fact that this must have been the start.
|
||||
*
|
||||
*/
|
||||
private Double getProbabilityForStartCandidateBasedOnOtherCompetitorsBehavior(Competitor c, TimePoint t) {
|
||||
final Double result;
|
||||
if (race.getStartOfRace(/* inferred */ false) == null && race.isGateStart() != Boolean.TRUE) {
|
||||
final Waypoint start = race.getRace().getCourse().getFirstWaypoint();
|
||||
final Iterable<Pair<Position, Bearing>> crossingInformationForStart = getCrossingInformation(start, t);
|
||||
if (start == null) {
|
||||
result = 1.0;
|
||||
} else {
|
||||
// if the start waypoint's passing instructions are unknown, assume it's a line
|
||||
final boolean startIsLine = getPassingInstructions(start) == PassingInstruction.Line;
|
||||
final List<AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine> distancesToStartLineOfOtherCompetitors = new ArrayList<>();
|
||||
for (final Competitor otherCompetitor : race.getRace().getCompetitors()) {
|
||||
if (otherCompetitor != c) {
|
||||
final DynamicGPSFixTrack<Competitor, GPSFixMoving> track = race.getTrack(otherCompetitor);
|
||||
if (track != null) {
|
||||
final Position estimatedPositionAtT = track.getEstimatedPosition(t, /* extrapolate */ true);
|
||||
final Distance otherCompetitorsDistanceToStartAtT = getMinDistanceOrNull(calculateDistance(estimatedPositionAtT, start, t));
|
||||
if (otherCompetitorsDistanceToStartAtT != null) {
|
||||
final Distance crossTrackError;
|
||||
if (startIsLine) {
|
||||
Pair<Position, Bearing> crossingInformationForStartLine = crossingInformationForStart.iterator().next();
|
||||
crossTrackError = estimatedPositionAtT.crossTrackError(crossingInformationForStartLine.getA(),
|
||||
crossingInformationForStartLine.getB());
|
||||
} else {
|
||||
crossTrackError = null;
|
||||
}
|
||||
distancesToStartLineOfOtherCompetitors.add(new
|
||||
AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine(otherCompetitorsDistanceToStartAtT, crossTrackError));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result = getProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances(distancesToStartLineOfOtherCompetitors, startIsLine);
|
||||
}
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method computes two probabilities of the candidate being a start: one based on the other competitors largely
|
||||
* being close to the start line, meaning a regular start where the competitor starts roughly at the same time as
|
||||
* everybody else; the other probability computed is based on the other competitors largely being in a similar
|
||||
* distance to the start line on the course side, suggesting that the competitor is starting late, but mostly
|
||||
* everybody else did start earlier and around the same time. The probability returned is the probability of
|
||||
* the start being the one <em>or</em> the other scenario which mathematically is represented by using the inverted
|
||||
* probabilities of the two start options, multiplying them and inverting the resulting probability.<p>
|
||||
*
|
||||
* Access needs to be protected to satisfy Maven-based test cases in com.sap.sailing.domain.test bundle which is not a fragment
|
||||
*/
|
||||
protected Double getProbabilityOfStartBasedOnOtherCompetitorsStartLineDistances(
|
||||
final List<AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine> distancesToStartLineOfOtherCompetitors, boolean startIsLine) {
|
||||
final Double result;
|
||||
// sort by the absolute distance to start line
|
||||
// boats within one hull length of the start line are great indicators that we're at the start:
|
||||
final Distance hullLength = race.getRace().getBoatClass().getHullLength();
|
||||
final Distance weightedAverageAbsoluteDistance;
|
||||
if (!distancesToStartLineOfOtherCompetitors.isEmpty()) {
|
||||
Collections.sort(distancesToStartLineOfOtherCompetitors, (a, b)->a.getAbsoluteGeometricDistance().compareTo(b.getAbsoluteGeometricDistance()));
|
||||
Distance weightedAbsoluteDistanceSum = Distance.NULL;
|
||||
int i=0;
|
||||
double weightSum = 0;
|
||||
for (final AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine d : distancesToStartLineOfOtherCompetitors) {
|
||||
final double weight = weight(((double) i)/distancesToStartLineOfOtherCompetitors.size());
|
||||
final Distance weightedDistance = d.getAbsoluteGeometricDistance().scale(weight);
|
||||
weightedAbsoluteDistanceSum = weightedAbsoluteDistanceSum.add(weightedDistance);
|
||||
weightSum += weight;
|
||||
i++;
|
||||
}
|
||||
weightedAverageAbsoluteDistance = weightedAbsoluteDistanceSum.scale(1. / weightSum);
|
||||
final double probabilityOfStartWithOthers = Math.max(WORST_PENALTY_FOR_OTHER_COMPETITORS_BEING_FAR_FROM_START, Math.min(1.0,
|
||||
(1.-(1.-WORST_PENALTY_FOR_OTHER_COMPETITORS_BEING_FAR_FROM_START)/NUMBER_OF_HULL_LENGTHS_DISTANCE_FROM_START_AT_WHICH_WORST_PENALTY_APPLIES
|
||||
* (weightedAverageAbsoluteDistance.divide(hullLength)-1))));
|
||||
if (startIsLine) {
|
||||
// Now look at the variance, particularly on course side: low variance may mean we are looking at a late starter
|
||||
// sort such that the boats with the least value (greatest negative amount) comes first and the last ~20% are largely ignored
|
||||
Collections.sort(distancesToStartLineOfOtherCompetitors, (a, b)->a.getSignedProjectedDistance().compareTo(b.getSignedProjectedDistance()));
|
||||
Distance weightedSignedDistanceSum = Distance.NULL;
|
||||
weightSum = 0;
|
||||
i=0;
|
||||
for (final AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine d : distancesToStartLineOfOtherCompetitors) {
|
||||
final double weight = weight(((double) i)/distancesToStartLineOfOtherCompetitors.size());
|
||||
weightedSignedDistanceSum = weightedSignedDistanceSum.add(d.getSignedProjectedDistance().scale(
|
||||
weight));
|
||||
weightSum += weight;
|
||||
i++;
|
||||
}
|
||||
final Distance weightedAverageSignedDistance = weightedSignedDistanceSum.scale(1. / weightSum);
|
||||
i=0;
|
||||
double signedDistanceVarianceSumInSquareMeters = 0;
|
||||
weightSum = 0;
|
||||
for (final AbsoluteGeometricDistanceAndSignedProjectedDistanceToStartLine d : distancesToStartLineOfOtherCompetitors) {
|
||||
final double differenceFromWeightedAverageInMeters = d.getSignedProjectedDistance().getMeters() - weightedAverageSignedDistance.getMeters();
|
||||
final double weight = weight(((double) i)/distancesToStartLineOfOtherCompetitors.size());
|
||||
signedDistanceVarianceSumInSquareMeters += weight*differenceFromWeightedAverageInMeters*differenceFromWeightedAverageInMeters;
|
||||
weightSum += weight;
|
||||
i++;
|
||||
}
|
||||
final double weightedVarianceInSquareMeters = signedDistanceVarianceSumInSquareMeters / weightSum;
|
||||
final double weightedVarianceRatioToSquaredHullLength = weightedVarianceInSquareMeters / hullLength.getMeters() / hullLength.getMeters();
|
||||
final double probabilityOfLateStart = Math.max(WORST_PENALTY_FOR_OTHER_COMPETITORS_BEING_FAR_FROM_START, Math.min(1.0,
|
||||
(1.-(1.-WORST_PENALTY_FOR_OTHER_COMPETITORS_BEING_FAR_FROM_START)/NUMBER_OF_HULL_LENGTHS_DISTANCE_FROM_START_AT_WHICH_WORST_PENALTY_APPLIES
|
||||
* (weightedVarianceRatioToSquaredHullLength-1))));
|
||||
result = 1. - (1.-probabilityOfLateStart) * (1.-probabilityOfStartWithOthers);
|
||||
} else {
|
||||
result = probabilityOfStartWithOthers;
|
||||
}
|
||||
} else {
|
||||
// no distance for any other competitor from the start line was available
|
||||
result = 1.0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Idea: sort and cut off the worst 20%. We played with weight functions for averaging the distances such that after sorting them
|
||||
* in increasing order the smaller ones get greater weight and the "outlier" ones towards the end of the sorted set receive small
|
||||
* weights. Still, if even the "best" (least) distances are far away, the result will be a low probability.
|
||||
* A function that could be helpful is this: -atan(20*(x-0.8)) + Pi/2)/Pi. The 0.8 represents the 80% starting from which the
|
||||
* weight declines sharply. The factor of 20 represents how sharp the decline is. x is between 0 and 1, representing the start
|
||||
* and the end of the sorted distance collection, respectively. Outputs are between 0 and 1, representing the weight to assign
|
||||
* to a distance while averaging.
|
||||
*/
|
||||
private double weight(double relativePositionInDistanceCollectionInIncreasingOrder) {
|
||||
final double SHARPNESS_OF_DECLINE = 100;
|
||||
final double CENTER_OF_DECLINE = 0.8;
|
||||
return (-Math.atan(SHARPNESS_OF_DECLINE*(relativePositionInDistanceCollectionInIncreasingOrder-CENTER_OF_DECLINE))+Math.PI/2) / Math.PI;
|
||||
}
|
||||
|
||||
private Distance getMinDistanceOrNull(List<Distance> distances) {
|
||||
Distance result = null;
|
||||
for (Distance d : distances) {
|
||||
if (d != null && (result == null || result.compareTo(d) > 0)) {
|
||||
result = d;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isStartWaypoint(Waypoint w) {
|
||||
return race.getRace().getCourse().getFirstWaypoint() == w;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -635,32 +909,38 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
* to be negative. If the passing instructions are {@link PassingInstruction#Line}, it checks whether the boat
|
||||
* passed between the two marks.
|
||||
*/
|
||||
private boolean isOnCorrectSideOfWaypoint(Waypoint w, Position p, TimePoint t, boolean portMark) {
|
||||
boolean result = true;
|
||||
private double getSidePenalty(Waypoint w, Position p, TimePoint t, boolean portMark) {
|
||||
boolean isOnRightSide = true;
|
||||
Distance onWrongSide = new MeterDistance(0);
|
||||
PassingInstruction instruction = getPassingInstructions(w);
|
||||
if (instruction == PassingInstruction.Line) {
|
||||
List<Position> pos = new ArrayList<>();
|
||||
for (Mark m : w.getMarks()) {
|
||||
Position po = race.getOrCreateTrack(m).getEstimatedPosition(t, false);
|
||||
if (po == null) {
|
||||
return true;
|
||||
isOnRightSide = true;
|
||||
break;
|
||||
}
|
||||
pos.add(po);
|
||||
}
|
||||
if (pos.size() != 2){
|
||||
return true;
|
||||
if (pos.size() != 2) {
|
||||
isOnRightSide = true;
|
||||
}
|
||||
Bearing diff1 = pos.get(0).getBearingGreatCircle(p)
|
||||
.getDifferenceTo(pos.get(0).getBearingGreatCircle(pos.get(1)));
|
||||
Bearing diff2 = pos.get(1).getBearingGreatCircle(p)
|
||||
.getDifferenceTo(pos.get(1).getBearingGreatCircle(pos.get(0)));
|
||||
Position leftMarkPos = pos.get(0);
|
||||
Position rightMarkPos = pos.get(1);
|
||||
Bearing diff1 = leftMarkPos.getBearingGreatCircle(p)
|
||||
.getDifferenceTo(leftMarkPos.getBearingGreatCircle(rightMarkPos));
|
||||
Bearing diff2 = rightMarkPos.getBearingGreatCircle(p)
|
||||
.getDifferenceTo(rightMarkPos.getBearingGreatCircle(leftMarkPos));
|
||||
if (Math.abs(diff1.getDegrees()) > 90 || Math.abs(diff2.getDegrees()) > 90) {
|
||||
result = false;
|
||||
isOnRightSide = false;
|
||||
Distance leftDistance = p.getDistance(leftMarkPos);
|
||||
Distance rightDistance = p.getDistance(rightMarkPos);
|
||||
onWrongSide = leftDistance.getMeters() < rightDistance.getMeters() ? leftDistance : rightDistance;
|
||||
}
|
||||
|
||||
} else {
|
||||
Mark m = null;
|
||||
if (instruction == PassingInstruction.Port || instruction == PassingInstruction.Starboard
|
||||
if (instruction == PassingInstruction.Single_Unknown || instruction == PassingInstruction.Port || instruction == PassingInstruction.Starboard
|
||||
|| instruction == PassingInstruction.FixedBearing || instruction == PassingInstruction.Offset) {
|
||||
m = w.getMarks().iterator().next();
|
||||
} else if (instruction == PassingInstruction.Gate) {
|
||||
@@ -669,27 +949,48 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
}
|
||||
if (m != null) {
|
||||
Util.Pair<Position, Bearing> crossingInfo = Util.get(getCrossingInformation(w, t), portMark ? 0 : 1);
|
||||
result = p.crossTrackError(crossingInfo.getA(), crossingInfo.getB().add(new DegreeBearingImpl(90)))
|
||||
.getMeters() < 0;
|
||||
isOnRightSide = p.crossTrackError(crossingInfo.getA(), crossingInfo.getB().add(new DegreeBearingImpl(90))).getMeters() < 0;
|
||||
if (isOnRightSide == false) {
|
||||
onWrongSide = p.getDistance(crossingInfo.getA());
|
||||
}
|
||||
}
|
||||
}
|
||||
final double result;
|
||||
if (isOnRightSide == true) {
|
||||
result = 1;
|
||||
} else {
|
||||
//TODO There should be a constant (either the 1.5 or the -0.2) which controls how strict the the curve is.
|
||||
result = (1-PENALTY_FOR_WRONG_SIDE) * Math.pow(1.5, -0.2 * onWrongSide.getMeters()) + PENALTY_FOR_WRONG_SIDE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a candidate passes a waypoint in the right direction. Can only be applied to XTE-Candidates.
|
||||
* For marks passed on port, the cross-track error should switch from positive to negative and vice versa. Lines are
|
||||
* also from positive to negative as the cross-track error to a line is always positive when approaching it from the
|
||||
* correct side.
|
||||
* Determines whether a candidate passes a waypoint in the right direction. A probability of 1 is assigned in case
|
||||
* it does. Otherwise, depending on the passing instructions, {@link #PENALTY_FOR_WRONG_DIRECTION} or
|
||||
* {@link #PENALTY_FOR_LINE_PASSED_IN_WRONG_DIRECTION} is returned as a "probability penalty" for passing the
|
||||
* waypoint in the wrong direction. The penalty is worse if {@link PassingInstruction#Line lines} are passed in the
|
||||
* wrong direction. Can only be applied to XTE-Candidates. For marks passed on port, the cross-track error should
|
||||
* switch from positive to negative and vice versa. Lines are also from positive to negative as the cross-track
|
||||
* error to a line is always positive when approaching it from the correct side.
|
||||
*
|
||||
* @return {@code null} in case the passing instructions aren't defined and therefore the correct direction is not
|
||||
* known
|
||||
*/
|
||||
private boolean passesInTheRightDirection(Waypoint w, double xte1, double xte2, boolean portMark) {
|
||||
boolean result = true;
|
||||
private Double passesInTheRightDirection(Waypoint w, double xte1, double xte2, boolean portMark) {
|
||||
final Double result;
|
||||
PassingInstruction instruction = getPassingInstructions(w);
|
||||
if (instruction == PassingInstruction.Port || instruction == PassingInstruction.Line
|
||||
if (instruction == PassingInstruction.Single_Unknown) {
|
||||
result = null;
|
||||
} else if (instruction == PassingInstruction.Port
|
||||
|| (instruction == PassingInstruction.Gate && portMark)) {
|
||||
result = xte1 > xte2 ? true : false;
|
||||
result = xte1 > xte2 ? 1 : PENALTY_FOR_WRONG_DIRECTION;
|
||||
} else if (instruction == PassingInstruction.Starboard || (instruction == PassingInstruction.Gate && !portMark)) {
|
||||
result = xte1 < xte2 ? true : false;
|
||||
result = xte1 < xte2 ? 1 : PENALTY_FOR_WRONG_DIRECTION;
|
||||
} else if (instruction == PassingInstruction.Line) {
|
||||
result = xte1 > xte2 ? 1 : PENALTY_FOR_LINE_PASSED_IN_WRONG_DIRECTION;
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -698,19 +999,21 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
* @return a probability based on the distance to <code>w</code> and the average leg lengths before and after.
|
||||
*/
|
||||
private Double getDistanceBasedProbability(Waypoint w, TimePoint t, Distance distance) {
|
||||
Distance legLength = getLegLength(t, w);
|
||||
final Double result;
|
||||
Distance legLength = getAverageLengthOfAdjacentLegs(t, w);
|
||||
if (legLength != null) {
|
||||
double result = 1 / (strictnessOfDistanceBasedProbability/* Raising this will make it stricter */
|
||||
result = 1 / (STRICTNESS_OF_DISTANCE_BASED_PROBABILITY/* Raising this will make it stricter */
|
||||
* Math.abs(distance.getMeters() / legLength.getMeters()) + 1);
|
||||
return result;
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return null;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an average of the estimated legs before and after <code>w</code>.
|
||||
*/
|
||||
private Distance getLegLength(TimePoint t, Waypoint w) {
|
||||
private Distance getAverageLengthOfAdjacentLegs(TimePoint t, Waypoint w) {
|
||||
Course course = race.getRace().getCourse();
|
||||
if (w == course.getFirstWaypoint()) {
|
||||
return race.getTrackedLegStartingAt(w).getGreatCircleDistance(t);
|
||||
@@ -730,11 +1033,12 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
* Calculates the distance from p to each mark of w at the timepoint t.
|
||||
*/
|
||||
private List<Distance> calculateDistance(Position p, Waypoint w, TimePoint t) {
|
||||
List<Distance> distances = new ArrayList<>();
|
||||
final List<Distance> distances = new ArrayList<>();
|
||||
PassingInstruction instruction = getPassingInstructions(w);
|
||||
boolean singleMark = false;
|
||||
switch (instruction) {
|
||||
case Port:
|
||||
case Single_Unknown:
|
||||
case Starboard:
|
||||
case FixedBearing:
|
||||
singleMark = true;
|
||||
@@ -760,7 +1064,7 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
Position portLinePosition = race.getOrCreateTrack(posLine.getA()).getEstimatedPosition(t, false);
|
||||
Position starboardLinePosition = race.getOrCreateTrack(posLine.getB()).getEstimatedPosition(t, false);
|
||||
distances.add((portLinePosition != null && starboardLinePosition != null) ? p.getDistanceToLine(
|
||||
portLinePosition, starboardLinePosition) : null);
|
||||
portLinePosition, starboardLinePosition).abs() : null);
|
||||
}
|
||||
break;
|
||||
case Offset:
|
||||
|
||||
+2
-1
@@ -38,7 +38,8 @@ public class CandidateImpl implements Candidate {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Candidate for waypoint " + getOneBasedIndexOfWaypoint() + " with cost " + getProbability() + " and Timepoint " + p;
|
||||
return "Candidate for waypoint " + getOneBasedIndexOfWaypoint() + " with probability " +
|
||||
getProbability() + " and timepoint " + getTimePoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.sap.sailing.domain.markpassingcalculation.impl;
|
||||
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public abstract class CandidateWithSide extends CandidateImpl {
|
||||
private final double onCorrectSideOfWaypoint;
|
||||
private final Double startProbabilityBasedOnOtherCompetitors;
|
||||
|
||||
public CandidateWithSide(int oneBasedIndexOfWaypoint, TimePoint p, double probability, Double startProbabilityBasedOnOtherCompetitors,
|
||||
Waypoint w, double onCorrectSideOfWaypoint) {
|
||||
super(oneBasedIndexOfWaypoint, p, probability, w);
|
||||
this.startProbabilityBasedOnOtherCompetitors = startProbabilityBasedOnOtherCompetitors;
|
||||
this.onCorrectSideOfWaypoint = onCorrectSideOfWaypoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ""+getClass().getSimpleName()+" "+super.toString()+", onCorrectSideOfWaypoint: "+onCorrectSideOfWaypoint+
|
||||
(startProbabilityBasedOnOtherCompetitors==null?"":", startProbabilityBasedOnOtherCompetitors="+startProbabilityBasedOnOtherCompetitors);
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.sap.sailing.domain.markpassingcalculation.impl;
|
||||
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class DistanceCandidateImpl extends CandidateWithSide {
|
||||
private final Distance distance;
|
||||
|
||||
public DistanceCandidateImpl(int oneBasedIndexOfWaypoint, TimePoint p, double probability, Double startProbabilityBasedOnOtherCompetitors,
|
||||
Waypoint w, double onCorrectSideOfWaypoint, Distance distance) {
|
||||
super(oneBasedIndexOfWaypoint, p, probability, startProbabilityBasedOnOtherCompetitors, w, onCorrectSideOfWaypoint);
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString()+", distance: "+distance;
|
||||
}
|
||||
}
|
||||
+38
-18
@@ -4,7 +4,7 @@ import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.markpassingcalculation.Candidate;
|
||||
|
||||
/**
|
||||
* Represent the passage between two {@link Candidate}s, <code>start</code> and <code>end</code>. {@link #getCost()}
|
||||
* Represent the passage between two {@link Candidate}s, <code>start</code> and <code>end</code>. {@link #getProbability()}
|
||||
* returns the probability of this passage being correct, consisting of the probability of each Candidate, the
|
||||
* probability of the passage between them (an estimate whether the distance sailed between the candidates matches the
|
||||
* distance between their {@link Waypoint}s), and a penalty for skipping waypoints, which might happen if a tracker
|
||||
@@ -13,14 +13,30 @@ import com.sap.sailing.domain.markpassingcalculation.Candidate;
|
||||
* @author Nicolas Klose
|
||||
*
|
||||
*/
|
||||
|
||||
public class Edge implements Comparable<Edge> {
|
||||
private final Candidate start;
|
||||
private final Candidate end;
|
||||
// TODO what is the meaning of this constant?
|
||||
private final static double penaltyForSkipped = 0.7;
|
||||
// TODO what is the meaning of this constant?
|
||||
private final static double penaltyForSkippedToEnd = 0.6;
|
||||
|
||||
/**
|
||||
* The penalty for an edge's probability in case the edge skips one or more waypoints and does not
|
||||
* end at the end proxy node. Skipping a waypoint that is not the last means that either the waypoint
|
||||
* was passed in a very strange, unrecognized way or we got a tracker outage. This is both pretty
|
||||
* unlikely and is penalized by this factor being multiplied to the edge's general probability.<p>
|
||||
*
|
||||
* This factor is raised to the n-th power for n waypoints skipped.
|
||||
*/
|
||||
private final static double PENALTY_FOR_SKIPPED = 0.1;
|
||||
|
||||
/**
|
||||
* Similar to {@link #PENALTY_FOR_SKIPPED}, but applied to edges that skip to the end proxy node. Such skips
|
||||
* are regular business while the race is still going on, and paths that are otherwise likely but don't lead
|
||||
* up to the end shall be possible and therefore must not be penalized harshly, receiving less of a penalty
|
||||
* (greater factor).<p>
|
||||
*
|
||||
* As with {@link #PENALTY_FOR_SKIPPED}, this factor is raised to the n-th power for n waypoints skipped.
|
||||
*/
|
||||
private final static double PENALTY_FOR_SKIPPED_TO_END = 0.5;
|
||||
|
||||
private final double estimatedDistanceAndStartTimingProbability;
|
||||
private final int numberOfWaypoints;
|
||||
|
||||
@@ -32,20 +48,22 @@ public class Edge implements Comparable<Edge> {
|
||||
}
|
||||
|
||||
public static double getPenaltyForSkipping() {
|
||||
return penaltyForSkipped;
|
||||
return PENALTY_FOR_SKIPPED;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cost for skipping a waypoint is 2*{@link #penaltyForSkipped} but is reduced to {@link #penaltyForSkippedToEnd} when
|
||||
* skipping to the end. The reason for preferring skips to the end proxy node is that in live situations where the course
|
||||
* hasn't been completed yet it is required to skip to the end. Additional cost comes from the inverse probability of
|
||||
* the product of the start node, end node and distance-based probabilities. If these probabilities multiply to zero,
|
||||
* an additional cost of 1.0 is added to any skip penalty.
|
||||
* The probability-reducing factor for skipping a waypoint is {@link #PENALTY_FOR_SKIPPED} but is reduced to
|
||||
* {@link #PENALTY_FOR_SKIPPED_TO_END} when skipping to the end. The reason for preferring skips to the end proxy node
|
||||
* is that in live situations where the course hasn't been completed yet it is required to skip to the end. The main
|
||||
* probability comes from the probability of the product of the start node, end node and distance-based
|
||||
* probabilities.
|
||||
*/
|
||||
public Double getCost() {
|
||||
double penalty = end.getOneBasedIndexOfWaypoint() == numberOfWaypoints + 1 ? penaltyForSkippedToEnd : penaltyForSkipped;
|
||||
return 1 - (start.getProbability() * end.getProbability() * estimatedDistanceAndStartTimingProbability) + 2 * penalty
|
||||
* (end.getOneBasedIndexOfWaypoint() - start.getOneBasedIndexOfWaypoint() - 1);
|
||||
public Double getProbability() {
|
||||
final double penalty = end.getOneBasedIndexOfWaypoint() == numberOfWaypoints + 1 ? PENALTY_FOR_SKIPPED_TO_END : PENALTY_FOR_SKIPPED;
|
||||
// See bug 3241 comment #38: only use the edge's end candidate's probability; the start candidate's probability is the
|
||||
// previous edge's end probability. The only probability we'll miss this way is that of the start proxy node and that is always 1.
|
||||
return end.getProbability() * estimatedDistanceAndStartTimingProbability *
|
||||
Math.pow(penalty, (end.getOneBasedIndexOfWaypoint() - start.getOneBasedIndexOfWaypoint() - 1));
|
||||
}
|
||||
|
||||
public Candidate getStart() {
|
||||
@@ -57,11 +75,13 @@ public class Edge implements Comparable<Edge> {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Edge from Waypoint " + start.getOneBasedIndexOfWaypoint() + " to " + end.getOneBasedIndexOfWaypoint() + ": " + getCost();
|
||||
return "Edge from Waypoint " + start.getOneBasedIndexOfWaypoint() + " (" +
|
||||
start.getTimePoint()+") to " + end.getOneBasedIndexOfWaypoint() + " (" +
|
||||
end.getTimePoint()+"): " + getProbability();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Edge o) {
|
||||
return start != o.getStart() ? start.compareTo(o.getStart()) : end != o.getEnd() ? end.compareTo(o.getEnd()) : getCost().compareTo(o.getCost());
|
||||
return start != o.getStart() ? start.compareTo(o.getStart()) : end != o.getEnd() ? end.compareTo(o.getEnd()) : getProbability().compareTo(o.getProbability());
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.sap.sailing.domain.markpassingcalculation.impl;
|
||||
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class XTECandidateImpl extends CandidateWithSide {
|
||||
private final Double passesInTheRightDirectionProbability;
|
||||
|
||||
public XTECandidateImpl(int oneBasedIndexOfWaypoint, TimePoint p, double probability,
|
||||
Double startProbabilityBasedOnOtherCompetitors, Waypoint w, double onCorrectSideOfWaypoint,
|
||||
Double passesInTheRightDirectionProbability) {
|
||||
super(oneBasedIndexOfWaypoint, p, probability, startProbabilityBasedOnOtherCompetitors, w, onCorrectSideOfWaypoint);
|
||||
this.passesInTheRightDirectionProbability = passesInTheRightDirectionProbability;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString()+", passesInTheRightDirection: "+passesInTheRightDirectionProbability;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,25 @@ import com.sap.sse.common.Timed;
|
||||
|
||||
public interface DynamicTrack<FixType extends Timed> extends Track<FixType> {
|
||||
/**
|
||||
* Tries to add {@code fix} to this track. If an {@link #equals(Object) equal} fix already exists
|
||||
* in this track, the track remains unchanged and {@code false} will be returned.
|
||||
*
|
||||
* @return <code>true</code> if the element was added, <code>false</code> otherwise.
|
||||
*/
|
||||
boolean add(FixType fix);
|
||||
|
||||
/**
|
||||
* Tries to add {@code fix} to this track. If an {@link #equals(Object) equal} fix already exists
|
||||
* in this track and {@code replace} is {@code false}, the track remains unchanged and {@code false} will be returned.
|
||||
* If {@code replace} is {@code true}, an equal (by definition of the comparator used for the fixes set) fix will
|
||||
* replace the fix contained in this track.
|
||||
*
|
||||
* @param replace
|
||||
* whether or not to replace an existing fix in the track that is equal to {@link #fix} as defined by the
|
||||
* comparator used for the {@link #fixes} set. By default this is a comparator only comparing the fixes'
|
||||
* time stamps. Subclasses may use different comparator implementations.
|
||||
*
|
||||
* @return <code>true</code> if the element was added, <code>false</code> otherwise.
|
||||
*/
|
||||
boolean add(FixType fix, boolean replace);
|
||||
}
|
||||
|
||||
@@ -101,6 +101,14 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
|
||||
* If no start time can be determined this way, <code>null</code> is returned.
|
||||
*/
|
||||
TimePoint getStartOfRace();
|
||||
|
||||
/**
|
||||
* Like {@link #getStartOfRace()}, but ignoring any inference from start mark passings in case {@code inferred} is
|
||||
* {@code false}. In this case, if no official start time was set, e.g., in the {@link RaceLog} or explicitly using
|
||||
* {@link #getStartTimeReceived()}, {@code null} will be returned by this method even if start mark passings are
|
||||
* present.
|
||||
*/
|
||||
TimePoint getStartOfRace(boolean inferred);
|
||||
|
||||
/**
|
||||
* Determine the race end time is tricky. Boats may sink, stop, not finish, although they started the race. We
|
||||
|
||||
+7
-2
@@ -47,12 +47,17 @@ public class DynamicGPSFixMovingTrackImpl<ItemType> extends GPSFixTrackImpl<Item
|
||||
*/
|
||||
@Override
|
||||
public void addGPSFix(GPSFixMoving gpsFix) {
|
||||
add(gpsFix);
|
||||
add(gpsFix, /* replace */ true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(GPSFixMoving fix) {
|
||||
return super.add(new CompactGPSFixMovingImpl(fix));
|
||||
return super.add(fix); // ends up calling this.add(fix, false) where conversion in CompactGPSFixMovingImpl will happen
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(GPSFixMoving fix, boolean replace) {
|
||||
return super.add(new CompactGPSFixMovingImpl(fix), replace);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+7
-2
@@ -12,12 +12,17 @@ public class DynamicGPSFixTrackImpl<ItemType> extends GPSFixTrackImpl<ItemType,
|
||||
}
|
||||
|
||||
public void addGPSFix(GPSFix gpsFix) {
|
||||
add(gpsFix);
|
||||
add(gpsFix, /* replace */ true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(GPSFix fix) {
|
||||
return super.add(new CompactGPSFixImpl(fix));
|
||||
return super.add(fix); // ends up calling this.add(fix, false) where conversion in CompactGPSFixImpl will happen
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(GPSFix fix, boolean replace) {
|
||||
return super.add(new CompactGPSFixImpl(fix), replace);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
@@ -17,4 +17,9 @@ public class DynamicTrackImpl<FixType extends Timed> extends TrackImpl<FixType>
|
||||
public boolean add(FixType fix) {
|
||||
return super.add(fix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(FixType fix, boolean replace) {
|
||||
return super.add(fix, replace);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1100,13 +1100,13 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean add(FixType fix) {
|
||||
protected boolean add(FixType fix, boolean replace) {
|
||||
final boolean result;
|
||||
final boolean firstFixInTrack;
|
||||
lockForWrite();
|
||||
try {
|
||||
firstFixInTrack = getRawFixes().isEmpty();
|
||||
result = addWithoutLocking(fix);
|
||||
result = addWithoutLocking(fix, replace);
|
||||
if (!validityCachingSuspended) {
|
||||
invalidateValidityAndEstimatedSpeedAndDistanceCaches(fix);
|
||||
}
|
||||
|
||||
+62
-20
@@ -202,7 +202,14 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
* The calculated race start time
|
||||
*/
|
||||
private TimePoint startTime;
|
||||
|
||||
|
||||
/**
|
||||
* Maintained in lock-step with {@link #startTime}, only that {@code null} will be contained if {@link #startTime}
|
||||
* was only inferred from start mark passings and no other, more official, information such as
|
||||
* {@link #getStartTimeReceived()} or a start time coming from an attached {@link RaceLog}.
|
||||
*/
|
||||
private TimePoint startTimeWithoutInferenceFromStartMarkPassings;
|
||||
|
||||
/**
|
||||
* The calculated race end time
|
||||
*/
|
||||
@@ -732,6 +739,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
|
||||
public void invalidateStartTime() {
|
||||
startTime = null;
|
||||
startTimeWithoutInferenceFromStartMarkPassings = null;
|
||||
}
|
||||
|
||||
public void invalidateEndTime() {
|
||||
@@ -771,13 +779,26 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
*/
|
||||
@Override
|
||||
public TimePoint getStartOfRace() {
|
||||
if (startTime == null) {
|
||||
return getStartOfRace(/* inferred */ true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getStartOfRace(boolean inferred) {
|
||||
final TimePoint result;
|
||||
// check if we have in our caching fields what is asked:
|
||||
if (inferred && startTime != null) {
|
||||
result = startTime;
|
||||
} else if (!inferred && startTimeWithoutInferenceFromStartMarkPassings != null) {
|
||||
result = startTimeWithoutInferenceFromStartMarkPassings;
|
||||
} else {
|
||||
// no cache match; re-calculate
|
||||
for (RaceLog raceLog : attachedRaceLogs.values()) {
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.finest("Analyzing race log "+raceLog+" for race "+this.getRace().getName());
|
||||
}
|
||||
startTime = new StartTimeFinder(raceLogResolver, raceLog).analyze().getStartTime();
|
||||
if (startTime != null) {
|
||||
startTimeWithoutInferenceFromStartMarkPassings = startTime;
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.finest("Found start time "+startTime+" in race log "+raceLog+" for race "+this.getRace().getName());
|
||||
}
|
||||
@@ -789,12 +810,16 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
logger.finest("No start time found in race logs for race "+getRace().getName());
|
||||
}
|
||||
startTime = getStartTimeReceived();
|
||||
if (startTime != null) {
|
||||
startTimeWithoutInferenceFromStartMarkPassings = startTime;
|
||||
}
|
||||
// If not null, check if the first mark passing for the start line is too much after the
|
||||
// startTimeReceived; if so, return an adjusted, later start time.
|
||||
// If no official start time was received, try to estimate the start time using the mark passings for
|
||||
// the start line.
|
||||
final Waypoint firstWaypoint;
|
||||
if (getTrackedRegatta().getRegatta().useStartTimeInference() && (firstWaypoint = getRace().getCourse().getFirstWaypoint()) != null) {
|
||||
// in this "if" branch update only startTime, not startTimeWithoutInferenceFromStartMarkPassings
|
||||
if (startTimeReceived != null) {
|
||||
// plausibility check for start time received, based on start mark passings; if no boat started within
|
||||
// a grace period of MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS after the start time
|
||||
@@ -828,8 +853,13 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inferred) {
|
||||
result = startTime;
|
||||
} else {
|
||||
result = startTimeWithoutInferenceFromStartMarkPassings;
|
||||
}
|
||||
}
|
||||
return startTime;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1498,7 +1528,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
return getOrCreateTrack(mark, false);
|
||||
}
|
||||
|
||||
private GPSFixTrack<Mark, GPSFix> getOrCreateTrack(Mark mark, boolean createIfNotExistent){
|
||||
private GPSFixTrack<Mark, GPSFix> getOrCreateTrack(Mark mark, boolean createIfNotExistent) {
|
||||
GPSFixTrack<Mark, GPSFix> result = markTracks.get(mark);
|
||||
if (result == null) {
|
||||
// try again, this time with more expensive synchronization
|
||||
@@ -3248,22 +3278,8 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
logger.info("Finished loading competitor tracks for " + getRace().getName());
|
||||
logger.info("Started loading mark tracks for " + getRace().getName());
|
||||
for (Mark mark : getMarksFromRegattaLogs()) {
|
||||
try {
|
||||
gpsFixStore.loadMarkTrack((DynamicGPSFixTrack<Mark, GPSFix>) getOrCreateTrack(mark),
|
||||
log, mark, startOfTimeWindowToLoad, endOfTimeWindowToLoad);
|
||||
if (getOrCreateTrack(mark).getFirstRawFix() == null) {
|
||||
logger.fine("Loading mark positions from outside of start/end of tracking interval ("+
|
||||
startOfTimeWindowToLoad+".."+endOfTimeWindowToLoad+
|
||||
") because no fixes were found in that interval");
|
||||
// got an empty track for the mark; try again without constraining the mapping interval
|
||||
// by start/end of tracking to at least attempt to get fixes at all in case there were any
|
||||
// within the device mapping interval specified
|
||||
gpsFixStore.loadMarkTrack((DynamicGPSFixTrack<Mark, GPSFix>) getOrCreateTrack(mark),
|
||||
log, mark, /* startOfTimeWindowToLoad */ null, /* endOfTimeWindowToLoad */ null);
|
||||
}
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load track for " + mark, e);
|
||||
}
|
||||
final DynamicGPSFixTrack<Mark, GPSFix> markTrack = (DynamicGPSFixTrack<Mark, GPSFix>) getOrCreateTrack(mark);
|
||||
loadMarkTrack(log, mark, markTrack, startOfTimeWindowToLoad, endOfTimeWindowToLoad);
|
||||
}
|
||||
logger.info("Finished loading mark tracks for " + getRace().getName());
|
||||
} finally {
|
||||
@@ -3287,6 +3303,32 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the GPS fixes for a mark from the {@link #gpsFixStore} and adds them to {@code intoTrack}. The device
|
||||
* mappings are expected to be found in {@code logWithDeviceMappings}. The fixes added have to be within
|
||||
* {@code from} and {@code to}, inclusively, with one exception: if no fix is found within this interval at all, all
|
||||
* fixes found for the mark are loaded to make sure that any position data that may give a hint for the interval
|
||||
* requests is loaded.
|
||||
*/
|
||||
private void loadMarkTrack(final RegattaLog logWithDeviceMappings, Mark mark,
|
||||
final DynamicGPSFixTrack<Mark, GPSFix> intoTrack, final TimePoint from, final TimePoint to) {
|
||||
try {
|
||||
gpsFixStore.loadMarkTrack(intoTrack, logWithDeviceMappings, mark, from, to);
|
||||
if (intoTrack.getFirstRawFix() == null) {
|
||||
logger.fine("Loading mark positions from outside of start/end of tracking interval ("+
|
||||
from+".."+to+
|
||||
") because no fixes were found in that interval");
|
||||
// got an empty track for the mark; try again without constraining the mapping interval
|
||||
// by start/end of tracking to at least attempt to get fixes at all in case there were any
|
||||
// within the device mapping interval specified
|
||||
gpsFixStore.loadMarkTrack(intoTrack,
|
||||
logWithDeviceMappings, mark, /* startOfTimeWindowToLoad */ null, /* endOfTimeWindowToLoad */ null);
|
||||
}
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load track for " + mark, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if currently the race is loading GPS fixes from the {@link GPSFixStore}. Clients may {@link Object#wait()} on <code>this</code>
|
||||
* object and will be notified whenever a change of this flag's value occurs.
|
||||
|
||||
+7
-2
@@ -108,9 +108,14 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Wind wind) {
|
||||
public boolean add(Wind fix) {
|
||||
return super.add(fix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Wind wind, boolean replace) {
|
||||
final CompactWindImpl compactWind = new CompactWindImpl(wind);
|
||||
final boolean result = super.add(compactWind);
|
||||
final boolean result = super.add(compactWind, replace);
|
||||
notifyListenersAboutReceive(compactWind);
|
||||
return result;
|
||||
}
|
||||
|
||||
+5
@@ -86,6 +86,11 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getStartOfRace(boolean inferred) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<TrackedLeg> getTrackedLegs() {
|
||||
return null;
|
||||
|
||||
Regular → Executable
+67
@@ -753,3 +753,70 @@ img.openColumn {
|
||||
.Media-Select-Popup .Caption {
|
||||
padding: 4px 10px;
|
||||
}
|
||||
|
||||
.EditMarkPositionPopup table {
|
||||
background-color: #fff;
|
||||
border-spacing: 0;
|
||||
border: 1px solid #aaa;
|
||||
|
||||
-webkit-box-shadow: 2px 2px 2px 0px rgba(0,0,0,0.5);
|
||||
-moz-box-shadow: 2px 2px 2px 0px rgba(0,0,0,0.5);
|
||||
box-shadow: 2px 2px 2px 0px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.EditMarkPositionPopup .gwt-MenuItem {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.EditMarkPositionPopup .gwt-MenuItem-disabled {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.EditMarkPositionConfirmCancelButtons .gwt-MenuItem {
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
padding: 5px 5px;
|
||||
background-color: #efab00;
|
||||
border: 1px solid gray;
|
||||
}
|
||||
|
||||
.EditMarkPositionConfirmCancelButtons .gwt-MenuItem:hover {
|
||||
background-color: #bf8b00;
|
||||
}
|
||||
|
||||
.EditMarkPositionConfirmCancelButtons table {
|
||||
border-spacing: 10px;
|
||||
border-collapse: seperate;
|
||||
}
|
||||
|
||||
.EditMarkPositionNotification > div {
|
||||
margin: 80px;
|
||||
font-size: 15px;
|
||||
padding: 20px;
|
||||
max-width: 500px;
|
||||
text-align: center;
|
||||
background-color: #fff;
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
.EditMarkPositionMarkTable {
|
||||
margin: 10px;
|
||||
min-width: 300px;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
border-left: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.EditMarkPositionMarkTable button {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
border: 1px solid gray;
|
||||
background-color: #efab00;
|
||||
padding: 7px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.EditMarkPositionMarkTable button:hover {
|
||||
background-color: #bf8b00;
|
||||
}
|
||||
@@ -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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.googlecode.java-diff-utils/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.json.simple/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.expeditionconnector/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.udpconnector/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.mongodb/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.operationaltransformation/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.replication/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.moxieapps.gwt.highcharts/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.geocoding/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.osgi/lib/gwt-user.jar" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.gwt.ui"/> </runtimeClasspathEntry> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt.adminconsole/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.polars.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.annotations/src" path="3" type="2"/> "/>
|
||||
</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 "${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work" -war "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -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"/>
|
||||
<?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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.googlecode.java-diff-utils/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.json.simple/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.expeditionconnector/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.udpconnector/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.mongodb/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.operationaltransformation/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.replication/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.moxieapps.gwt.highcharts/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.geocoding/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.osgi/lib/gwt-user.jar" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.gwt.ui"/> </runtimeClasspathEntry> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt.adminconsole/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.polars.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.annotations/src" path="3" type="2"/> "/>
|
||||
</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 "${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work" -war "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -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>
|
||||
</launchConfiguration>
|
||||
|
||||
@@ -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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.googlecode.java-diff-utils/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.json.simple/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.expeditionconnector/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.udpconnector/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.mongodb/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.operationaltransformation/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.replication/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.moxieapps.gwt.highcharts/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.geocoding/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.osgi/lib/gwt-user.jar" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.gwt.ui"/> </runtimeClasspathEntry> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt.adminconsole/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.polars.datamining.shared/src" path="3" type="2"/> "/>
|
||||
</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 "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -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"/>
|
||||
<?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="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.googlecode.java-diff-utils/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.json.simple/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.expeditionconnector/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/resources" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.udpconnector/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.mongodb/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.operationaltransformation/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.replication/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter.persistence/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.moxieapps.gwt.highcharts/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.geocoding/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.shared/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.osgi/lib/gwt-user.jar" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.gwt.ui"/> </runtimeClasspathEntry> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security.ui/src/main/java" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt.adminconsole/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.security.common/src" path="3" type="2"/> "/>
|
||||
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.polars.datamining.shared/src" path="3" type="2"/> "/>
|
||||
</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 "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -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>
|
||||
</launchConfiguration>
|
||||
|
||||
-1
@@ -139,7 +139,6 @@ public interface TextMessages extends Messages {
|
||||
String latestRaceResultsHeadline();
|
||||
String latestRaceResultsIsNew();
|
||||
String latestRaceResultsIsUpdated();
|
||||
String latestRaceResultsRaceCountLabel();
|
||||
String latestRaceResultsRaceCountUnit();
|
||||
String latestRaceResultsRefresh();
|
||||
String leaderboardQuickAccessLabel();
|
||||
|
||||
-1
@@ -128,7 +128,6 @@ headerSponsoring=Sponsoring
|
||||
latestRaceResultsHeadline=Latest Official Race Results
|
||||
latestRaceResultsIsNew=New
|
||||
latestRaceResultsIsUpdated=Updated
|
||||
latestRaceResultsRaceCountLabel=Result after
|
||||
latestRaceResultsRaceCountUnit=races
|
||||
latestRaceResultsRefresh=refresh
|
||||
leaderboardQuickAccessLabel=Leaderboard Quick Access
|
||||
|
||||
-1
@@ -128,7 +128,6 @@ headerSponsoring=Sponsoren
|
||||
latestRaceResultsHeadline=Letzten ofiziellen Ergebnisse
|
||||
latestRaceResultsIsNew=Neu
|
||||
latestRaceResultsIsUpdated=Aktualisiert
|
||||
latestRaceResultsRaceCountLabel=Ergebnis nach
|
||||
latestRaceResultsRaceCountUnit=Rennen
|
||||
latestRaceResultsRefresh=aktualisieren
|
||||
leaderboardQuickAccessLabel=Ergebnisliste Schnellzugang
|
||||
|
||||
-1
@@ -128,7 +128,6 @@ headerSponsoring=Sponsoring
|
||||
latestRaceResultsHeadline=Latest Official Race Results
|
||||
latestRaceResultsIsNew=New
|
||||
latestRaceResultsIsUpdated=Updated
|
||||
latestRaceResultsRaceCountLabel=Result after
|
||||
latestRaceResultsRaceCountUnit=races
|
||||
latestRaceResultsRefresh=refresh
|
||||
leaderboardQuickAccessLabel=Leaderboard Quick Access
|
||||
|
||||
-1
@@ -159,7 +159,6 @@ headerSponsoring=Sponsoring
|
||||
latestRaceResultsHeadline=Latest Official Race Results
|
||||
latestRaceResultsIsNew=New
|
||||
latestRaceResultsIsUpdated=Updated
|
||||
latestRaceResultsRaceCountLabel=Result after
|
||||
latestRaceResultsRaceCountUnit=races
|
||||
latestRaceResultsRefresh=refresh
|
||||
leaderboardQuickAccessLabel=Leaderboard Quick Access
|
||||
|
||||
+2
@@ -922,6 +922,8 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
|
||||
private String getPassingInstructionsAsText(PassingInstruction passingInstructions) {
|
||||
switch (passingInstructions) {
|
||||
case Single_Unknown:
|
||||
return stringMessages.toSide() + " " + stringMessages.unknown();
|
||||
case Port:
|
||||
return stringMessages.toSide() + " " + stringMessages.portSide();
|
||||
case Starboard:
|
||||
|
||||
+3
@@ -69,6 +69,9 @@ interface AdminConsoleResources extends ClientBundle {
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/start_racelog_tracking.png")
|
||||
ImageResource startRaceLogTracking();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/stop_racelog_tracking.png")
|
||||
ImageResource stopRaceLogTracking();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/competitor_registrations.png")
|
||||
ImageResource competitorRegistrations();
|
||||
|
||||
+22
-1
@@ -4,6 +4,7 @@ import com.google.gwt.event.dom.client.KeyUpEvent;
|
||||
import com.google.gwt.event.dom.client.KeyUpHandler;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeEvent;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeHandler;
|
||||
import com.google.gwt.regexp.shared.RegExp;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
|
||||
@@ -23,13 +24,18 @@ public abstract class BaseQRIdentifierWidget implements IsWidget {
|
||||
private final QRCodeComposite qrCodeComposite;
|
||||
private final Widget baseWidget;
|
||||
private final Label error;
|
||||
private final Label url;
|
||||
final Label url;
|
||||
private RegExp urlValidator = null;
|
||||
private RegExp urlPlusTldValidator = null;
|
||||
StringMessages stringMessages;
|
||||
|
||||
public BaseQRIdentifierWidget(int qrCodeSize, StringMessages stringMessages) {
|
||||
this(qrCodeSize, stringMessages, QRCodeComposite.ERROR_CORRECTION_LEVEL_H);
|
||||
}
|
||||
|
||||
public BaseQRIdentifierWidget(int qrCodeSize, StringMessages stringMessages, int errorCorrectionLevel) {
|
||||
this.stringMessages = stringMessages;
|
||||
|
||||
serverBox = new TextBox();
|
||||
serverBox.setVisibleLength(40);
|
||||
serverBox.setValue(Window.Location.getProtocol() + "//" + Window.Location.getHost());
|
||||
@@ -93,6 +99,21 @@ public abstract class BaseQRIdentifierWidget implements IsWidget {
|
||||
return serverUrl;
|
||||
}
|
||||
|
||||
|
||||
//from: https://stackoverflow.com/questions/4498225/gwt-java-url-validator#4499412 as java.net.URL not available in GWT
|
||||
private boolean isValidUrl(String url, boolean topLevelDomainRequired) {
|
||||
if (urlValidator == null || urlPlusTldValidator == null) {
|
||||
urlValidator = RegExp.compile("^((ftp|http|https)://[\\w@.\\-\\_]+(:\\d{1,5})?(/[\\w#!:.?+=&%@!\\_\\-/]+)*){1}$");
|
||||
urlPlusTldValidator = RegExp.compile("^((ftp|http|https)://[\\w@.\\-\\_]+\\.[a-zA-Z]{2,}(:\\d{1,5})?(/[\\w#!:.?+=&%@!\\_\\-/]+)*){1}$");
|
||||
}
|
||||
return (topLevelDomainRequired ? urlPlusTldValidator : urlValidator).exec(url) != null;
|
||||
}
|
||||
|
||||
public boolean isServerUrlValid(){
|
||||
String url = getServerUrlWithoutFinalSlash();
|
||||
return isValidUrl(url, false);
|
||||
}
|
||||
|
||||
protected void clear() {
|
||||
qrCodeComposite.clearQRCode();
|
||||
url.setText("");
|
||||
|
||||
+7
-2
@@ -38,12 +38,17 @@ public class DeviceMappingQRCodeWidget extends BaseQRIdentifierWidget {
|
||||
|
||||
@Override
|
||||
protected String generateEncodedQRCodeContent() throws QRCodeURLCreationException {
|
||||
if (!isServerUrlValid()){
|
||||
super.url.setText("");
|
||||
throw new QRCodeURLCreationException(stringMessages.serverURLInvalid());
|
||||
}
|
||||
|
||||
String serverUrl = getServerUrlWithoutFinalSlash();
|
||||
if (serverUrl.isEmpty()) {
|
||||
throw new QRCodeURLCreationException("Server URL empty");
|
||||
throw new QRCodeURLCreationException(stringMessages.serverURLEmpty());
|
||||
}
|
||||
if (mappedItemId == null) {
|
||||
throw new QRCodeURLCreationException("No item selected for mapping");
|
||||
throw new QRCodeURLCreationException(stringMessages.pleaseSelectAnItemToMapTo());
|
||||
}
|
||||
|
||||
return urlFactory.createURL(serverUrl, mappedItemType, mappedItemId);
|
||||
|
||||
+1
-1
@@ -217,7 +217,7 @@ public class RegattaLogAddDeviceMappingDialog extends DataEntryDialogWithBootstr
|
||||
}
|
||||
|
||||
private void loadCompetitorsAndMarks() {
|
||||
sailingService.getCompetitorRegistrationsInRegattaLog(leaderboardName, itemSelectionPanel.getSetCompetitorsCallback());
|
||||
sailingService.getCompetitorRegistrationsForLeaderboard(leaderboardName, itemSelectionPanel.getSetCompetitorsCallback());
|
||||
sailingService.getMarksInRegattaLog(leaderboardName, itemSelectionPanel.getSetMarksCallback());
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -103,7 +103,7 @@ public class RegattaLogImportFixesAndAddMappingsDialog extends DataEntryDialog<C
|
||||
}
|
||||
|
||||
void getCompetitorRegistrations(SailingServiceAsync sailingService, final ErrorReporter errorReporter) {
|
||||
sailingService.getCompetitorRegistrationsInRegattaLog(leaderboardName,
|
||||
sailingService.getCompetitorRegistrationsForLeaderboard(leaderboardName,
|
||||
new AsyncCallback<Collection<CompetitorDTO>>() {
|
||||
@Override
|
||||
public void onSuccess(Collection<CompetitorDTO> result) {
|
||||
|
||||
+22
-11
@@ -40,6 +40,8 @@ import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public class RegattaLogTrackingDeviceMappingsDialog extends DataEntryDialog<Void> {
|
||||
private static final int HOURS_TO_EXPAND_FOR_OPEN_END = 2;
|
||||
|
||||
protected final String leaderboardName;
|
||||
|
||||
public static final double PERCENTAGE_OF_TIMESPAN_TO_EXTEND_OPEN_ENDS = 0.1;
|
||||
@@ -165,16 +167,19 @@ public class RegattaLogTrackingDeviceMappingsDialog extends DataEntryDialog<Void
|
||||
}
|
||||
|
||||
void updateChart() {
|
||||
earliest = new Date(Long.MAX_VALUE);
|
||||
latest = new Date(Long.MIN_VALUE);
|
||||
earliest = null;
|
||||
latest = null;
|
||||
for (DeviceMappingDTO mapping : mappings) {
|
||||
updateExtremes(mapping);
|
||||
}
|
||||
data = new Point[mappings.size()];
|
||||
|
||||
handleOpenEndedIntervalls();
|
||||
|
||||
long earliestMillis = earliest.getTime();
|
||||
long latestMillis = latest.getTime();
|
||||
long range = latestMillis - earliestMillis;
|
||||
long extension = (long) (range * PERCENTAGE_OF_TIMESPAN_TO_EXTEND_OPEN_ENDS); // TODO bug 3426: open intervals dominate the chart too badly
|
||||
long extension = (long) (range * PERCENTAGE_OF_TIMESPAN_TO_EXTEND_OPEN_ENDS);
|
||||
long yMin = earliestMillis - extension;
|
||||
long yMax = latestMillis + extension;
|
||||
int i = 0;
|
||||
@@ -192,20 +197,26 @@ public class RegattaLogTrackingDeviceMappingsDialog extends DataEntryDialog<Void
|
||||
.setPoints(data));
|
||||
chart.getYAxis().setExtremes(yMin, yMax);
|
||||
}
|
||||
|
||||
private void handleOpenEndedIntervalls() {
|
||||
if (earliest == null && latest == null){
|
||||
//allMappingsAreOpenEnded in Both directions or no mappings present
|
||||
earliest = new Date(0);
|
||||
latest = new Date();
|
||||
} else if (earliest == null && latest != null){
|
||||
earliest = new Date(latest.getTime() - HOURS_TO_EXPAND_FOR_OPEN_END*1000*60*60);
|
||||
} else if (latest == null && earliest != null){
|
||||
latest = new Date(earliest.getTime() + HOURS_TO_EXPAND_FOR_OPEN_END*1000*60*60);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateExtremes(DeviceMappingDTO mapping) {
|
||||
if (mapping.from != null && earliest.after(mapping.from)) {
|
||||
if (mapping.from != null && (earliest == null || earliest.after(mapping.from))) {
|
||||
earliest = mapping.from;
|
||||
}
|
||||
if (mapping.to != null && latest.before(mapping.to)) {
|
||||
if (mapping.to != null && (latest == null || latest.before(mapping.to))) {
|
||||
latest = mapping.to;
|
||||
}
|
||||
if (mapping.to != null && earliest.after(mapping.to)) {
|
||||
earliest = mapping.to;
|
||||
}
|
||||
if (mapping.from != null && latest.before(mapping.from)) {
|
||||
latest = mapping.from;
|
||||
}
|
||||
}
|
||||
|
||||
private void addMapping() {
|
||||
|
||||
+77
-7
@@ -1,11 +1,14 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
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.DateTimeFormat.PredefinedFormat;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.CaptionPanel;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.HasVerticalAlignment;
|
||||
@@ -14,6 +17,7 @@ import com.google.gwt.user.client.ui.Image;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.PushButton;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.ToggleButton;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.gwt.ui.client.DataEntryDialogWithBootstrap;
|
||||
@@ -46,6 +50,7 @@ public class SetTrackingTimesDialog extends DataEntryDialogWithBootstrap<RaceLog
|
||||
|
||||
private BetterDateTimeBox startTimeBox;
|
||||
private BetterDateTimeBox endTimeBox;
|
||||
private ToggleButton startStopButton;
|
||||
private TextBox authorNameBox;
|
||||
private com.sap.sse.gwt.client.controls.IntegerBox authorPriorityBox;
|
||||
|
||||
@@ -85,6 +90,13 @@ public class SetTrackingTimesDialog extends DataEntryDialogWithBootstrap<RaceLog
|
||||
currentEnd = result == null ? null : result.getB();
|
||||
updateDateTimeLabelAndTimeBoxFromDate(currentStart, currentStartLabel, startTimeBox);
|
||||
updateDateTimeLabelAndTimeBoxFromDate(currentEnd, currentEndLabel, endTimeBox);
|
||||
if (currentStart == null){
|
||||
setupStartStopButton(false);
|
||||
} else if (currentStart != null && currentEnd == null){
|
||||
setupStartStopButton(true);
|
||||
} else {
|
||||
setupStartStopButton(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -92,24 +104,49 @@ public class SetTrackingTimesDialog extends DataEntryDialogWithBootstrap<RaceLog
|
||||
private void updateDateTimeLabelAndTimeBoxFromDate(final TimePoint timePoint, final Label label, final BetterDateTimeBox dateTimeBox) {
|
||||
if (timePoint == null) {
|
||||
label.setText(stringMessages.notAvailable());
|
||||
dateTimeBox.setValue(null);
|
||||
} else {
|
||||
label.setText(dateTimeFormat.format(timePoint == null ? null : timePoint.asDate()));
|
||||
dateTimeBox.setValue(timePoint==null ? null : timePoint.asDate());
|
||||
label.setText(dateTimeFormat.format(timePoint.asDate()));
|
||||
dateTimeBox.setValue(timePoint.asDate());
|
||||
}
|
||||
}
|
||||
|
||||
private void setupStartStopButton(boolean startTimeSet) {
|
||||
startStopButton.setDown(startTimeSet);
|
||||
}
|
||||
|
||||
private Widget createInputPanel() {
|
||||
Grid content = new Grid(4, 2);
|
||||
Grid content = new Grid(4, 3);
|
||||
|
||||
Button startNow = new Button(stringMessages.now());
|
||||
startNow.addClickHandler(new ClickHandler() {
|
||||
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
startTimeBox.setValue(new Date());
|
||||
}
|
||||
});
|
||||
Button endNow = new Button(stringMessages.now());
|
||||
endNow.addClickHandler(new ClickHandler() {
|
||||
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
endTimeBox.setValue(new Date());
|
||||
}
|
||||
});
|
||||
|
||||
startTimeBox = createDateTimeBox(null);
|
||||
startTimeBox.setFormat("dd/mm/yyyy hh:ii:ss");
|
||||
content.setWidget(0, 0, createLabel(stringMessages.startOfTracking()));
|
||||
content.setWidget(0, 1, startTimeBox);
|
||||
|
||||
content.setWidget(0, 2, startNow);
|
||||
|
||||
endTimeBox = createDateTimeBox(null);
|
||||
endTimeBox.setFormat("dd/mm/yyyy hh:ii:ss");
|
||||
content.setWidget(1, 0, createLabel(stringMessages.endOfTracking()));
|
||||
content.setWidget(1, 1, endTimeBox);
|
||||
|
||||
content.setWidget(1, 2, endNow);
|
||||
|
||||
authorNameBox = createTextBox("Shore");
|
||||
content.setWidget(2, 0, createLabel(stringMessages.authorName()));
|
||||
content.setWidget(2, 1, authorNameBox);
|
||||
@@ -142,6 +179,35 @@ public class SetTrackingTimesDialog extends DataEntryDialogWithBootstrap<RaceLog
|
||||
}
|
||||
});
|
||||
currentPanel.add(refreshButton);
|
||||
|
||||
Image startImage = new Image(resources.startRaceLogTracking());
|
||||
Image stopImage = new Image(resources.stopRaceLogTracking());
|
||||
|
||||
startStopButton = new ToggleButton(startImage, stopImage, new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
RaceLogSetTrackingTimesDTO trackingTimesDTO;
|
||||
if (startStopButton.isDown()){
|
||||
trackingTimesDTO = generateRaceLogSetTrackingTimesDTOWith(new Date(), null);
|
||||
} else {
|
||||
trackingTimesDTO = generateRaceLogSetTrackingTimesDTOWith(startTimeBox.getValue(), new Date());
|
||||
}
|
||||
|
||||
service.setTrackingTimes(trackingTimesDTO, new AsyncCallback<Void>(){
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError("Error while setting tracking times: " + caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
refreshTimes();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
currentPanel.add(startStopButton);
|
||||
|
||||
current.add(currentPanel);
|
||||
return current;
|
||||
@@ -149,6 +215,10 @@ public class SetTrackingTimesDialog extends DataEntryDialogWithBootstrap<RaceLog
|
||||
|
||||
@Override
|
||||
protected RaceLogSetTrackingTimesDTO getResult() {
|
||||
return generateRaceLogSetTrackingTimesDTOWith(startTimeBox.getValue(), endTimeBox.getValue());
|
||||
}
|
||||
|
||||
private RaceLogSetTrackingTimesDTO generateRaceLogSetTrackingTimesDTOWith(Date startTime, Date endTime) {
|
||||
RaceLogSetTrackingTimesDTO dto = new RaceLogSetTrackingTimesDTO();
|
||||
dto.leaderboardName = leaderboardName;
|
||||
dto.raceColumnName = raceColumnName;
|
||||
@@ -156,8 +226,8 @@ public class SetTrackingTimesDialog extends DataEntryDialogWithBootstrap<RaceLog
|
||||
dto.authorName = authorNameBox.getValue();
|
||||
dto.authorPriority = authorPriorityBox.getValue();
|
||||
dto.logicalTimePoint = MillisecondsTimePoint.now();
|
||||
dto.newStartOfTracking = startTimeBox.getValue() == null ? null : new MillisecondsTimePoint(startTimeBox.getValue());
|
||||
dto.newEndOfTracking = endTimeBox.getValue() == null ? null : new MillisecondsTimePoint(endTimeBox.getValue());
|
||||
dto.newStartOfTracking = startTime == null ? null : new MillisecondsTimePoint(startTime);
|
||||
dto.newEndOfTracking = endTime == null ? null : new MillisecondsTimePoint(endTime);
|
||||
dto.currentStartOfTracking = currentStart;
|
||||
dto.currentEndOfTracking = currentEnd;
|
||||
return dto;
|
||||
|
||||
Regular → Executable
+24
-4
@@ -46,6 +46,7 @@ import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.gwt.ui.adminconsole.RaceLogSetTrackingTimesDTO;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.MarkPositionService.MarkTracksDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.BulkScoreCorrectionDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.CompactBoatPositionsDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.CompactRaceMapDataDTO;
|
||||
@@ -59,6 +60,7 @@ import com.sap.sailing.gwt.ui.shared.DeviceMappingDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.EventBaseDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTOWithSpeedWindTackAndLegType;
|
||||
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
@@ -269,7 +271,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
|
||||
|
||||
String[] getCountryCodes();
|
||||
|
||||
Map<CompetitorDTO, List<GPSFixDTO>> getDouglasPoints(RegattaAndRaceIdentifier raceIdentifier,
|
||||
Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> getDouglasPoints(RegattaAndRaceIdentifier raceIdentifier,
|
||||
Map<CompetitorDTO, Date> from, Map<CompetitorDTO, Date> to, double meters) throws NoWindException;
|
||||
|
||||
Map<CompetitorDTO, List<ManeuverDTO>> getManeuvers(RegattaAndRaceIdentifier raceIdentifier,
|
||||
@@ -520,13 +522,13 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
|
||||
* pairs of zero-based waypoint numbers and times where <code>null</code> represents a suppressed mark
|
||||
* passing and a valid {@link Date} objects represents a fixed mark passing.
|
||||
*/
|
||||
Map<Integer, Date> getCompetitorRaceLogMarkPassingData(String leaderboardName, RaceColumnDTO raceColumnDTO, FleetDTO fleet,
|
||||
Map<Integer, Date> getCompetitorRaceLogMarkPassingData(String leaderboardName, String raceColumnName, String fleetName,
|
||||
CompetitorDTO competitor);
|
||||
|
||||
void updateSuppressedMarkPassings(String leaderboardName, RaceColumnDTO raceColumnDTO, FleetDTO fleet,
|
||||
void updateSuppressedMarkPassings(String leaderboardName, String raceColumnName, String fleetName,
|
||||
Integer newZeroBasedIndexOfSuppressedMarkPassing, CompetitorDTO competitorDTO);
|
||||
|
||||
void updateFixedMarkPassing(String leaderboardName, RaceColumnDTO raceColumnDTO, FleetDTO fleet, Integer indexOfWaypoint,
|
||||
void updateFixedMarkPassing(String leaderboardName, String raceColumnName, String fleetName, Integer indexOfWaypoint,
|
||||
Date dateOfMarkPassing, CompetitorDTO competitorDTO);
|
||||
|
||||
void setCompetitorRegistrationsInRegattaLog(String leaderboardName, Set<CompetitorDTO> competitors)
|
||||
@@ -619,4 +621,22 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
|
||||
String fleetName) throws NotFoundException;
|
||||
|
||||
Collection<CompetitorDTO> getCompetitorRegistrationsForLeaderboard(String leaderboardName) throws NotFoundException;
|
||||
|
||||
MarkTracksDTO getMarkTracks(String leaderboardName, String raceColumnName, String fleetName);
|
||||
|
||||
/**
|
||||
* The service may decide whether a mark fix can be removed. It may, for example, be impossible to
|
||||
* cleanly remove a mark fix if a tracked race already exists and the mark fixes are already part of
|
||||
* the GPS fix track which currently does not support a remove operation. However, when only the
|
||||
* regatta log is the basis of the service and no tracked race exists yet, mark fixes may be removed
|
||||
* by revoking the device mappings.
|
||||
*/
|
||||
boolean canRemoveMarkFix(String leaderboardName, String raceColumnName, String fleetName, String markIdAsString, GPSFixDTO fix);
|
||||
|
||||
void removeMarkFix(String leaderboardName, String raceColumnName, String fleetName, String markIdAsString, GPSFixDTO fix) throws NotRevokableException;
|
||||
|
||||
void addMarkFix(String leaderboardName, String raceColumnName, String fleetName, String markIdAsString, GPSFixDTO newFix);
|
||||
|
||||
void editMarkFix(String leaderboardName, String raceColumnName, String fleetName, String markIdAsString, GPSFixDTO oldFix, Position newPosition) throws NotRevokableException;
|
||||
|
||||
}
|
||||
|
||||
Regular → Executable
+21
-4
@@ -35,6 +35,7 @@ import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO;
|
||||
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.gwt.ui.adminconsole.RaceLogSetTrackingTimesDTO;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.MarkPositionService.MarkTracksDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.BulkScoreCorrectionDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.CompactBoatPositionsDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.CompactRaceMapDataDTO;
|
||||
@@ -48,6 +49,7 @@ import com.sap.sailing.gwt.ui.shared.DeviceMappingDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.EventBaseDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTOWithSpeedWindTackAndLegType;
|
||||
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
@@ -309,7 +311,7 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan
|
||||
void getCountryCodes(AsyncCallback<String[]> callback);
|
||||
|
||||
void getDouglasPoints(RegattaAndRaceIdentifier raceIdentifier, Map<CompetitorDTO, Date> from,
|
||||
Map<CompetitorDTO, Date> to, double meters, AsyncCallback<Map<CompetitorDTO, List<GPSFixDTO>>> callback);
|
||||
Map<CompetitorDTO, Date> to, double meters, AsyncCallback<Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>>> callback);
|
||||
|
||||
void getManeuvers(RegattaAndRaceIdentifier raceIdentifier, Map<CompetitorDTO, Date> from,
|
||||
Map<CompetitorDTO, Date> to, AsyncCallback<Map<CompetitorDTO, List<ManeuverDTO>>> callback);
|
||||
@@ -682,15 +684,15 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan
|
||||
void getCompetitorMarkPassings(RegattaAndRaceIdentifier race, CompetitorDTO competitorDTO,
|
||||
AsyncCallback<Map<Integer, Date>> callback);
|
||||
|
||||
void getCompetitorRaceLogMarkPassingData(String leaderboardName, RaceColumnDTO raceColumnDTO, FleetDTO fleet,
|
||||
void getCompetitorRaceLogMarkPassingData(String leaderboardName, String raceColumnName, String fleetName,
|
||||
CompetitorDTO competitor, AsyncCallback<Map<Integer, Date>> callback);
|
||||
|
||||
void updateSuppressedMarkPassings(String leaderboardName, RaceColumnDTO raceColumnDTO, FleetDTO fleet,
|
||||
void updateSuppressedMarkPassings(String leaderboardName, String raceColumnName, String fleetName,
|
||||
Integer newZeroBasedIndexOfSuppressedMarkPassing, CompetitorDTO competitorDTO, AsyncCallback<Void> callback);
|
||||
|
||||
void createRegattaStructure(Iterable<RegattaDTO> regattaNames, EventDTO newEvent, AsyncCallback<Void> asyncCallback);
|
||||
|
||||
void updateFixedMarkPassing(String leaderboardName, RaceColumnDTO raceColumnDTO, FleetDTO fleet,
|
||||
void updateFixedMarkPassing(String leaderboardName, String raceColumnName, String fleetName,
|
||||
Integer indexOfWaypoint, Date dateOfMarkPassing, CompetitorDTO competitorDTO, AsyncCallback<Void> callback);
|
||||
|
||||
void getActiveFileStorageServiceName(AsyncCallback<String> callback);
|
||||
@@ -763,4 +765,19 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan
|
||||
|
||||
void getCompetitorRegistrationsForLeaderboard(String leaderboardName,
|
||||
AsyncCallback<Collection<CompetitorDTO>> callback);
|
||||
|
||||
void addMarkFix(String leaderboardName, String raceColumnName, String fleetName, String markIdAsString,
|
||||
GPSFixDTO newFix, AsyncCallback<Void> callback);
|
||||
|
||||
void editMarkFix(String leaderboardName, String raceColumnName, String fleetName, String markIdAsString,
|
||||
GPSFixDTO oldFix, Position newPosition, AsyncCallback<Void> callback);
|
||||
|
||||
void removeMarkFix(String leaderboardName, String raceColumnName, String fleetName, String markIdAsString,
|
||||
GPSFixDTO fix, AsyncCallback<Void> callback);
|
||||
|
||||
void canRemoveMarkFix(String leaderboardName, String raceColumnName, String fleetName, String markIdAsString,
|
||||
GPSFixDTO fix, AsyncCallback<Boolean> callback);
|
||||
|
||||
void getMarkTracks(String leaderboardName, String raceColumnName, String fleetName,
|
||||
AsyncCallback<MarkTracksDTO> callback);
|
||||
}
|
||||
|
||||
+21
@@ -1221,6 +1221,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c
|
||||
String timeBetweenRaceStartAndCompetitorStartInSecondsTooltip();
|
||||
String useInternalAlgorithm();
|
||||
String editMarkPassings();
|
||||
String editMarkPositions();
|
||||
String removeFixedPassings();
|
||||
String setFixedPassing();
|
||||
String setSuppressedPassing();
|
||||
@@ -1502,4 +1503,24 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c
|
||||
String thereIsNoIdentifierSet();
|
||||
String couldNotObtainAccessTokenForUser(String message);
|
||||
String raceManagerDeviceName();
|
||||
String serverURLInvalid();
|
||||
String serverURLEmpty();
|
||||
String marks();
|
||||
String addNewFix();
|
||||
String pleaseSelectOtherTimepoint();
|
||||
String confirmMove();
|
||||
String confirmNewFix();
|
||||
String selectAFixPositionBy();
|
||||
String pleaseSelectAMark();
|
||||
String markFixes();
|
||||
String distanceFromAveragePosition();
|
||||
String useATouchOptimizedUI();
|
||||
String deleteFix();
|
||||
String theDeletionOfThisFix();
|
||||
String loadingMarkFixes();
|
||||
String fixSuccessfullyAdded();
|
||||
String fixPositionSuccessfullyEdited();
|
||||
String fixSuccessfullyRemoved();
|
||||
String dragToChangePosition();
|
||||
String moveFix();
|
||||
}
|
||||
|
||||
+22
-1
@@ -1189,6 +1189,7 @@ timeBetweenRaceStartAndCompetitorStartInSeconds=Start delay
|
||||
timeBetweenRaceStartAndCompetitorStartInSecondsTooltip=Time between the start of the race and the time the competitor passed the start waypoint. Negative time suggests OCS.
|
||||
useInternalAlgorithm=Use internal mark passing algorithm
|
||||
editMarkPassings=Edit Mark Passings
|
||||
editMarkPositions=Edit Mark Positions
|
||||
removeFixedPassings=Remove fixed mark passing
|
||||
setFixedPassing=Set time as mark passing
|
||||
setSuppressedPassing=Suppress mark passing starting with current
|
||||
@@ -1469,4 +1470,24 @@ defaultSailorsInfoLinkInfoText=The default link is used unless a language specif
|
||||
qrSync=QR-Sync
|
||||
thereIsNoIdentifierSet=There is no identifier set.
|
||||
couldNotObtainAccessTokenForUser=Could not obtain access token for user {0}
|
||||
raceManagerDeviceName=Device Name
|
||||
raceManagerDeviceName=Device Name
|
||||
serverURLInvalid=Please enter a valid Server URL
|
||||
serverURLEmpty=Server URL empty
|
||||
marks=Marks
|
||||
addNewFix=Add new fix
|
||||
pleaseSelectOtherTimepoint=Please select another timepoint. There already is a fix at the timepoint of the timeslider.
|
||||
confirmMove=Confirm move
|
||||
confirmNewFix=Confirm new fix
|
||||
selectAFixPositionBy=Select a fix position by dragging the map. Then confirm or cancel with the buttons below.
|
||||
pleaseSelectAMark=Please select a mark from the list on the left.
|
||||
markFixes=Mark fixes
|
||||
distanceFromAveragePosition=Distance from average position
|
||||
useATouchOptimizedUI=Use a touch optimized UI to move the fix
|
||||
deleteFix=Delete fix
|
||||
theDeletionOfThisFix=The deletion of this fix is currently not possible.
|
||||
loadingMarkFixes=Loading mark fixes...
|
||||
fixSuccessfullyAdded=Fix successfully added.
|
||||
fixPositionSuccessfullyEdited=Fix position successfully edited.
|
||||
fixSuccessfullyRemoved=Fix successfully removed.
|
||||
dragToChangePosition=Drag to change position\nRight-click to get menu to delete
|
||||
moveFix=Move fix
|
||||
|
||||
+23
-2
@@ -28,7 +28,7 @@ timeDelayHelp=Leer für Wiedergabe zu beliebigen Zeitpunkten; Verzögerung des D
|
||||
speedInKnots=Geschwindigkeit (sm/h)
|
||||
fromDeg=aus (Grad)
|
||||
noRacesYet=Es sind keine Wettfahrten vorhanden.
|
||||
total=Gesamt
|
||||
total=\u03A3
|
||||
carry=übertr.
|
||||
competitor=Team
|
||||
competitors=Teams
|
||||
@@ -1176,6 +1176,7 @@ timeBetweenRaceStartAndCompetitorStartInSeconds=Startverzug
|
||||
timeBetweenRaceStartAndCompetitorStartInSecondsTooltip=Zeit seit dem Start der Wettfahrt, nach der der Teilnehmer die Startmarke passiert hat. Negative Werte suggerieren einen Frühstart.
|
||||
useInternalAlgorithmus=Internen Algorithmus für Tonnenumrundungen verwenden
|
||||
editMarkPassings=Tonnenumrundungen editieren
|
||||
editMarkPositions=Tonnenpositionen editieren
|
||||
removeFixedPassings=Gesetze Umrundungszeit entfernen
|
||||
setFixedPassing=Zeit als Umrundungszeit setzen
|
||||
setSuppressedPassing=Umrundungen unterdrücken
|
||||
@@ -1460,4 +1461,24 @@ defaultSailorsInfoLinkInfoText=Der Standardwert wird verwendet, sofern kein Spra
|
||||
qrSync=QR-Sync
|
||||
thereIsNoIdentifierSet=Es ist noch keine Kennung gesetzt.
|
||||
couldNotObtainAccessTokenForUser=Zugangscode für Benutzer {0} konnte nicht bezogen werden
|
||||
raceManagerDeviceName=Gerätename
|
||||
raceManagerDeviceName=Gerätename
|
||||
serverURLInvalid=Bitte gültige Server-URL eingeben
|
||||
serverURLEmpty=Server URL darf nicht leer sein
|
||||
marks=Bahnmarken
|
||||
addNewFix=Neuen Fix hinzufügen
|
||||
pleaseSelectOtherTimepoint=Bitte wählen Sie einen anderen Zeitpunkt. Am gewählten Zeitpunkt ist bereits ein Fix.
|
||||
confirmMove=Bestätige Umpositionierung
|
||||
confirmNewFix=Bestätige neuen Fix
|
||||
selectAFixPositionBy=Wählen sie eine Fixposition, indem sie die Karte verschieben. Bestätigen oder Brechen sie danach mit den Knöpfen unten ab.
|
||||
pleaseSelectAMark=Bitte wählen sie einen Marker aus der Liste auf der linken Seite aus.
|
||||
markFixes=Marker Fixe
|
||||
distanceFromAveragePosition=Entfernung vom Durchschnitt
|
||||
useATouchOptimizedUI=Benutzen sie eine auf Touchbildschirme optimierte Eingabe.
|
||||
deleteFix=Fix löschen
|
||||
theDeletionOfThisFix=The Löschung des Fixes is aktuell nicht möglich.
|
||||
loadingMarkFixes=Lade Marker Fixe...
|
||||
fixSuccessfullyAdded=Fix erfolgreich hinzugefügt.
|
||||
fixPositionSuccessfullyEdited=Fixposition erfolgreich editiert.
|
||||
fixSuccessfullyRemoved=Fix erfolgreich bewegt.
|
||||
dragToChangePosition=Ziehen um die Position zu verändern\nRechtsklick für Menü mit Löschfunktion
|
||||
moveFix=Position durch Ziehen ändern
|
||||
|
||||
+22
-1
@@ -1119,6 +1119,7 @@ timeBetweenRaceStartAndCompetitorStartInSeconds=Start delay
|
||||
timeBetweenRaceStartAndCompetitorStartInSecondsTooltip=Time between the start of the race and the time the competitor passed the start waypoint. Negative time suggests OCS.
|
||||
useInternalAlgorithm=Use internal mark passing algorithm
|
||||
editMarkPassings=Edit Mark Passings
|
||||
editMarkPositions=Edit Mark Positions
|
||||
removeFixedPassings=Remove fixed mark passing
|
||||
setFixedPassing=Set time as mark passing
|
||||
setSuppressedPassing=Suppress mark passing starting with current
|
||||
@@ -1391,4 +1392,24 @@ defaultSailorsInfoLinkInfoText=The default link is used unless a language specif
|
||||
qrSync=QR-Sync
|
||||
thereIsNoIdentifierSet=There is no identifier set.
|
||||
couldNotObtainAccessTokenForUser=Could not obtain access token for user {0}
|
||||
raceManagerDeviceName=Device Name
|
||||
raceManagerDeviceName=Device Name
|
||||
serverURLInvalid=Please enter a valid Server URL
|
||||
serverURLEmpty=Server URL empty
|
||||
marks=Marks
|
||||
addNewFix=Add new fix
|
||||
pleaseSelectOtherTimepoint=Please select another timepoint. There already is a fix at the timepoint of the timeslider.
|
||||
confirmMove=Confirm move
|
||||
confirmNewFix=Confirm new fix
|
||||
selectAFixPositionBy=Select a fix position by dragging the map. Then confirm or cancel with the buttons below.
|
||||
pleaseSelectAMark=Please select a mark from the list on the left.
|
||||
markFixes=Mark fixes
|
||||
distanceFromAveragePosition=Distance from average position
|
||||
useATouchOptimizedUI=Use a touch optimized UI to move the fix
|
||||
deleteFix=Delete fix
|
||||
theDeletionOfThisFix=The deletion of this fix is currently not possible.
|
||||
loadingMarkFixes=Loading mark fixes...
|
||||
fixSuccessfullyAdded=Fix successfully added.
|
||||
fixPositionSuccessfullyEdited=Fix position successfully edited.
|
||||
fixSuccessfullyRemoved=Fix successfully removed.
|
||||
dragToChangePosition=Drag to change position\nRight-click to get menu to delete
|
||||
moveFix=Move fix
|
||||
|
||||
+23
-2
@@ -157,7 +157,7 @@ addRegatta=Add Regatta...
|
||||
hideToolbar=Hide toolbar
|
||||
tracTracUpdateUrl=TracTrac Server Update URI
|
||||
apply=Apply
|
||||
total=u03A3
|
||||
total=\u03A3
|
||||
explainReplicationServletPort=Servlet port number that is used to connect to register this replica and receive initial load.
|
||||
numberOfManeuvers=操作
|
||||
successfullyAllowedCompetitorReset=Successfully allowed reload of competitor(s) {0}.
|
||||
@@ -1158,6 +1158,7 @@ explainConnectionsToMaster=Connections to a master (this server is a replica):
|
||||
errorStartingReplication=Error starting replication from host {0} / exchange {1}: {2}
|
||||
rotatedFromTrueNorthClickToToggleWindUp=Rotated {0} degrees from true north.nTap/click to toggle between wind-up and north-up map display
|
||||
editMarkPassings=Edit Mark Passings
|
||||
editMarkPositions=Edit Mark Positions
|
||||
stepSizeMustBeGreaterThanNull=Step size must be greater than 0
|
||||
hideRaces=隐藏比赛轮次
|
||||
speedOverGroundFiveSecondsBeforeStartTooltip=起航前5秒时的实际速度
|
||||
@@ -1406,4 +1407,24 @@ defaultSailorsInfoLinkInfoText=The default link is used unless a language specif
|
||||
qrSync=QR-Sync
|
||||
thereIsNoIdentifierSet=There is no identifier set.
|
||||
couldNotObtainAccessTokenForUser=Could not obtain access token for user {0}
|
||||
raceManagerDeviceName=Device Name
|
||||
raceManagerDeviceName=Device Name
|
||||
serverURLInvalid=Please enter a valid Server URL
|
||||
serverURLEmpty=Server URL empty
|
||||
marks=Marks
|
||||
addNewFix=Add new fix
|
||||
pleaseSelectOtherTimepoint=Please select another timepoint. There already is a fix at the timepoint of the timeslider.
|
||||
confirmMove=Confirm move
|
||||
confirmNewFix=Confirm new fix
|
||||
selectAFixPositionBy=Select a fix position by dragging the map. Then confirm or cancel with the buttons below.
|
||||
pleaseSelectAMark=Please select a mark from the list on the left.
|
||||
markFixes=Mark fixes
|
||||
distanceFromAveragePosition=Distance from average position
|
||||
useATouchOptimizedUI=Use a touch optimized UI to move the fix
|
||||
deleteFix=Delete fix
|
||||
theDeletionOfThisFix=The deletion of this fix is currently not possible.
|
||||
loadingMarkFixes=Loading mark fixes...
|
||||
fixSuccessfullyAdded=Fix successfully added.
|
||||
fixPositionSuccessfullyEdited=Fix position successfully edited.
|
||||
fixSuccessfullyRemoved=Fix successfully removed.
|
||||
dragToChangePosition=Drag to change position\nRight-click to get menu to delete
|
||||
moveFix=Move fix
|
||||
|
||||
+2
-4
@@ -427,10 +427,7 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
|
||||
assert min != null && max != null;
|
||||
|
||||
boolean changed = false;
|
||||
if (!max.equals(timeRangeProvider.getToTime()) || !min.equals(timeRangeProvider.getFromTime())) {
|
||||
changed = true;
|
||||
timeSlider.setMinAndMaxValue(new Double(min.getTime()),new Double(max.getTime()), fireEvent);
|
||||
}
|
||||
changed = timeSlider.setMinAndMaxValue(new Double(min.getTime()), new Double(max.getTime()), fireEvent);
|
||||
if (changed) {
|
||||
if (!timeRangeProvider.isZoomed()) {
|
||||
timeRangeProvider.setTimeRange(min, max, this);
|
||||
@@ -528,6 +525,7 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
|
||||
|
||||
@Override
|
||||
public void onTimeRangeChanged(Date fromTime, Date toTime) {
|
||||
setMinMax(fromTime, toTime, true);
|
||||
}
|
||||
|
||||
protected boolean isLiveModeToBeMadePossible() {
|
||||
|
||||
+2
-9
@@ -108,7 +108,7 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
|
||||
Timer timer, TimeRangeWithZoomProvider timeRangeWithZoomProvider, final StringMessages stringMessages,
|
||||
ErrorReporter errorReporter, DetailType detailType, boolean compactChart, boolean allowTimeAdjust,
|
||||
String leaderboardGroupName, String leaderboardName) {
|
||||
super(sailingService, timer, timeRangeWithZoomProvider, stringMessages, asyncActionsExecutor, errorReporter);
|
||||
super(sailingService, selectedRaceIdentifier, timer, timeRangeWithZoomProvider, stringMessages, asyncActionsExecutor, errorReporter);
|
||||
this.competitorSelectionProvider = competitorSelectionProvider;
|
||||
this.compactChart = compactChart;
|
||||
this.allowTimeAdjust = allowTimeAdjust;
|
||||
@@ -126,7 +126,6 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
|
||||
createChart();
|
||||
setSelectedDetailType(detailType);
|
||||
competitorSelectionProvider.addCompetitorSelectionChangeListener(this);
|
||||
this.selectedRaceIdentifier = selectedRaceIdentifier;
|
||||
clearChart();
|
||||
if (selectedRaceIdentifier != null) {
|
||||
timeChanged(timer.getTime(), null);
|
||||
@@ -505,7 +504,7 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
|
||||
if (oldReversedYAxis != hasReversedYAxis(selectedDetailType)) {
|
||||
chart = createChart();
|
||||
if (isZoomed) {
|
||||
com.sap.sse.common.Util.Pair<Date, Date> zoomRange = timeRangeWithZoomProvider.getTimeZoom();
|
||||
com.sap.sse.common.Util.Pair<Date, Date> zoomRange = timeRangeWithZoomProvider.getTimeZoom();
|
||||
onTimeZoomChanged(zoomRange.getA(), zoomRange.getB());
|
||||
} else {
|
||||
resetMinMaxAndExtremesInterval(/* redraw */ true);
|
||||
@@ -632,12 +631,6 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTimePlotLine(Date date) {
|
||||
chart.getXAxis().removePlotLine(timePlotLine);
|
||||
timePlotLine.setValue(date.getTime());
|
||||
chart.getXAxis().addPlotLines(timePlotLine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResize() {
|
||||
chart.setSizeToMatchContainer();
|
||||
|
||||
+39
-24
@@ -11,6 +11,8 @@ import org.moxieapps.gwt.highcharts.client.XAxis;
|
||||
import org.moxieapps.gwt.highcharts.client.events.ChartClickEvent;
|
||||
import org.moxieapps.gwt.highcharts.client.events.ChartSelectionEvent;
|
||||
|
||||
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.i18n.client.DateTimeFormat;
|
||||
import com.google.gwt.user.client.ui.AbsolutePanel;
|
||||
@@ -45,7 +47,7 @@ public abstract class AbstractRaceChart extends AbsolutePanel implements TimeLis
|
||||
protected final Timer timer;
|
||||
protected final TimeRangeWithZoomProvider timeRangeWithZoomProvider;
|
||||
|
||||
protected RegattaAndRaceIdentifier selectedRaceIdentifier;
|
||||
protected final RegattaAndRaceIdentifier selectedRaceIdentifier;
|
||||
|
||||
protected final DateTimeFormat dateFormat = DateTimeFormat.getFormat("HH:mm:ss");
|
||||
protected final DateTimeFormat dateFormatHoursMinutes = DateTimeFormat.getFormat("HH:mm");
|
||||
@@ -69,9 +71,10 @@ public abstract class AbstractRaceChart extends AbsolutePanel implements TimeLis
|
||||
|
||||
private final Button settingsButton;
|
||||
|
||||
protected AbstractRaceChart(SailingServiceAsync sailingService, Timer timer, TimeRangeWithZoomProvider timeRangeWithZoomProvider, final StringMessages stringMessages,
|
||||
AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter) {
|
||||
protected AbstractRaceChart(SailingServiceAsync sailingService, RegattaAndRaceIdentifier selectedRaceIdentifier, Timer timer, TimeRangeWithZoomProvider timeRangeWithZoomProvider,
|
||||
final StringMessages stringMessages, AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter) {
|
||||
this.sailingService = sailingService;
|
||||
this.selectedRaceIdentifier = selectedRaceIdentifier;
|
||||
this.timer = timer;
|
||||
this.timeRangeWithZoomProvider = timeRangeWithZoomProvider;
|
||||
this.stringMessages = stringMessages;
|
||||
@@ -155,13 +158,17 @@ public abstract class AbstractRaceChart extends AbsolutePanel implements TimeLis
|
||||
}
|
||||
timeRangeWithZoomProvider.setTimeZoom(new Date(xAxisMin), new Date(xAxisMax), this);
|
||||
} catch (Exception e) {
|
||||
// in case the user clicks the "reset zoom" button chartSelectionEvent.getXAxisMinAsLong() throws in exception
|
||||
timeRangeWithZoomProvider.resetTimeZoom(this);
|
||||
// Trigger the redrawing... otherwise chart wouldn't reset the zoom
|
||||
chart.redraw();
|
||||
isZoomed = false;
|
||||
// after the selection change event, another click event is sent with the mouse position on the "Reset Zoom" button; ignore that
|
||||
ignoreNextClickEvent = true;
|
||||
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
|
||||
@Override
|
||||
public void execute() {
|
||||
// in case the user clicks the "reset zoom" button chartSelectionEvent.getXAxisMinAsLong() throws in exception
|
||||
isZoomed = false;
|
||||
timeRangeWithZoomProvider.resetTimeZoom();
|
||||
// redraw is triggered by the call to onTimeZoomReset() and therefore not necessary again here
|
||||
// after the selection change event, another click event is sent with the mouse position on the "Reset Zoom" button; ignore that
|
||||
ignoreNextClickEvent = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -179,20 +186,22 @@ public abstract class AbstractRaceChart extends AbsolutePanel implements TimeLis
|
||||
}
|
||||
|
||||
protected void changeMinMaxAndExtremesInterval(Date minTimepoint, Date maxTimepoint, boolean redraw) {
|
||||
XAxis xAxis = chart.getXAxis();
|
||||
if (minTimepoint != null) {
|
||||
xAxis.setMin(minTimepoint.getTime());
|
||||
}
|
||||
if (maxTimepoint != null) {
|
||||
xAxis.setMax(maxTimepoint.getTime());
|
||||
}
|
||||
if (minTimepoint != null && maxTimepoint != null) {
|
||||
xAxis.setExtremes(minTimepoint.getTime(), maxTimepoint.getTime(), false, false);
|
||||
long tickInterval = (maxTimepoint.getTime() - minTimepoint.getTime()) / TICKCOUNT;
|
||||
xAxis.setTickInterval(tickInterval);
|
||||
}
|
||||
if (redraw) {
|
||||
chart.redraw();
|
||||
if (chart != null) {
|
||||
XAxis xAxis = chart.getXAxis();
|
||||
if (minTimepoint != null) {
|
||||
xAxis.setMin(minTimepoint.getTime());
|
||||
}
|
||||
if (maxTimepoint != null) {
|
||||
xAxis.setMax(maxTimepoint.getTime());
|
||||
}
|
||||
if (minTimepoint != null && maxTimepoint != null) {
|
||||
xAxis.setExtremes(minTimepoint.getTime(), maxTimepoint.getTime(), /* redraw */ false, false);
|
||||
long tickInterval = (maxTimepoint.getTime() - minTimepoint.getTime()) / TICKCOUNT;
|
||||
xAxis.setTickInterval(tickInterval);
|
||||
}
|
||||
if (redraw) {
|
||||
chart.redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,4 +238,10 @@ public abstract class AbstractRaceChart extends AbsolutePanel implements TimeLis
|
||||
public void onTimeZoomReset() {
|
||||
resetMinMaxAndExtremesInterval(true);
|
||||
}
|
||||
|
||||
protected void updateTimePlotLine(Date date) {
|
||||
chart.getXAxis().removePlotLine(timePlotLine);
|
||||
timePlotLine.setValue(date.getTime());
|
||||
chart.getXAxis().addPlotLines(timePlotLine);
|
||||
}
|
||||
}
|
||||
|
||||
+102
-94
@@ -29,11 +29,11 @@ import com.google.gwt.view.client.SingleSelectionModel;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.RaceIdentifierToLeaderboardRaceColumnAndFleetMapper.LeaderboardNameRaceColumnNameAndFleetName;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceCourseDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.WaypointDTO;
|
||||
import com.sap.sse.common.Util;
|
||||
@@ -60,7 +60,7 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
private final ErrorReporter errorReporter;
|
||||
private final StringMessages stringMessages;
|
||||
private final CompetitorSelectionProvider competitorSelectionModel;
|
||||
private LeaderboardDTO leaderboard;
|
||||
private final RaceIdentifierToLeaderboardRaceColumnAndFleetMapper raceIdentifierToLeaderboardRaceColumnAndFleetMapper;
|
||||
|
||||
private CompetitorDTO competitor;
|
||||
|
||||
@@ -88,6 +88,7 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
public EditMarkPassingsPanel(final SailingServiceAsync sailingService, final RegattaAndRaceIdentifier raceIdentifier,
|
||||
final StringMessages stringMessages, final CompetitorSelectionProvider competitorSelectionModel,
|
||||
final ErrorReporter errorReporter, final Timer timer) {
|
||||
this.raceIdentifierToLeaderboardRaceColumnAndFleetMapper = new RaceIdentifierToLeaderboardRaceColumnAndFleetMapper();
|
||||
this.sailingService = sailingService;
|
||||
this.raceIdentifier = raceIdentifier;
|
||||
this.errorReporter = errorReporter;
|
||||
@@ -164,39 +165,49 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
removeFixedMarkPassingsButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
final RaceColumnDTO column = getColumn();
|
||||
sailingService.updateFixedMarkPassing(getLeaderboardName(), column, column.getFleet(raceIdentifier),
|
||||
final LeaderboardNameRaceColumnNameAndFleetName leaderboardNameRaceColumnNameAndFleetName =
|
||||
raceIdentifierToLeaderboardRaceColumnAndFleetMapper.getLeaderboardNameAndRaceColumnNameAndFleetName(raceIdentifier);
|
||||
if (leaderboardNameRaceColumnNameAndFleetName != null) {
|
||||
sailingService.updateFixedMarkPassing(leaderboardNameRaceColumnNameAndFleetName.getLeaderboardName(),
|
||||
leaderboardNameRaceColumnNameAndFleetName.getRaceColumnName(),
|
||||
leaderboardNameRaceColumnNameAndFleetName.getFleetName(),
|
||||
waypointSelectionModel.getSelectedObject().getA(), null, competitor, new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorRemovingFixedPassing(caught.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
refillList();
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorRemovingFixedPassing(caught.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
refillList();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
setTimeAsMarkPassingsButton = new Button(stringMessages.setFixedPassing());
|
||||
setTimeAsMarkPassingsButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
final RaceColumnDTO column = getColumn();
|
||||
sailingService.updateFixedMarkPassing(getLeaderboardName(), column, column.getFleet(raceIdentifier),
|
||||
waypointSelectionModel.getSelectedObject().getA(), timer.getTime(), competitor,
|
||||
new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorSettingFixedPassing(caught.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
refillList();
|
||||
}
|
||||
});
|
||||
final LeaderboardNameRaceColumnNameAndFleetName leaderboardNameRaceColumnNameAndFleetName =
|
||||
raceIdentifierToLeaderboardRaceColumnAndFleetMapper.getLeaderboardNameAndRaceColumnNameAndFleetName(raceIdentifier);
|
||||
if (leaderboardNameRaceColumnNameAndFleetName != null) {
|
||||
sailingService.updateFixedMarkPassing(leaderboardNameRaceColumnNameAndFleetName.getLeaderboardName(),
|
||||
leaderboardNameRaceColumnNameAndFleetName.getRaceColumnName(),
|
||||
leaderboardNameRaceColumnNameAndFleetName.getFleetName(),
|
||||
waypointSelectionModel.getSelectedObject().getA(), timer.getTime(), competitor,
|
||||
new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorSettingFixedPassing(caught.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
refillList();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -205,20 +216,25 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
suppressPassingsButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
final RaceColumnDTO column = getColumn();
|
||||
sailingService.updateSuppressedMarkPassings(getLeaderboardName(), column,
|
||||
column.getFleet(raceIdentifier), waypointSelectionModel.getSelectedObject().getA(),
|
||||
competitor, new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorSuppressingPassing(caught.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
refillList();
|
||||
}
|
||||
});
|
||||
final LeaderboardNameRaceColumnNameAndFleetName leaderboardNameRaceColumnNameAndFleetName =
|
||||
raceIdentifierToLeaderboardRaceColumnAndFleetMapper.getLeaderboardNameAndRaceColumnNameAndFleetName(raceIdentifier);
|
||||
if (leaderboardNameRaceColumnNameAndFleetName != null) {
|
||||
sailingService.updateSuppressedMarkPassings(leaderboardNameRaceColumnNameAndFleetName.getLeaderboardName(),
|
||||
leaderboardNameRaceColumnNameAndFleetName.getRaceColumnName(),
|
||||
leaderboardNameRaceColumnNameAndFleetName.getFleetName(),
|
||||
waypointSelectionModel.getSelectedObject().getA(),
|
||||
competitor, new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorSuppressingPassing(caught.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
refillList();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -226,20 +242,25 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
removeSuppressedPassingButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
final RaceColumnDTO column = getColumn();
|
||||
sailingService.updateSuppressedMarkPassings(getLeaderboardName(), column,
|
||||
column.getFleet(raceIdentifier), null, competitor,
|
||||
final LeaderboardNameRaceColumnNameAndFleetName leaderboardNameRaceColumnNameAndFleetName =
|
||||
raceIdentifierToLeaderboardRaceColumnAndFleetMapper.getLeaderboardNameAndRaceColumnNameAndFleetName(raceIdentifier);
|
||||
if (leaderboardNameRaceColumnNameAndFleetName != null) {
|
||||
sailingService.updateSuppressedMarkPassings(leaderboardNameRaceColumnNameAndFleetName.getLeaderboardName(),
|
||||
leaderboardNameRaceColumnNameAndFleetName.getRaceColumnName(),
|
||||
leaderboardNameRaceColumnNameAndFleetName.getFleetName(),
|
||||
null, competitor,
|
||||
new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorRemovingSupressedPassing(caught.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
refillList();
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorRemovingSupressedPassing(caught.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
refillList();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
selectCompetitorLabel.setText(stringMessages.selectCompetitor());
|
||||
@@ -265,7 +286,6 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
public void setVisible(boolean visible) {
|
||||
processCompetitorSelectionChange(visible);
|
||||
super.setVisible(visible);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -314,27 +334,32 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
waypointList.getList().clear();
|
||||
waypointList.getList().addAll(newMarkPassings);
|
||||
// Get current edits
|
||||
final RaceColumnDTO column = getColumn();
|
||||
sailingService.getCompetitorRaceLogMarkPassingData(getLeaderboardName(), column,
|
||||
column.getFleet(raceIdentifier), competitor, new AsyncCallback<Map<Integer, Date>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorTryingToObtainRaceLogMarkPassingData(caught.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Map<Integer, Date> result) {
|
||||
for (Entry<Integer, Date> data : result.entrySet()) {
|
||||
if (data.getValue() == null) {
|
||||
zeroBasedIndexOfFirstSuppressedWaypoint = data.getKey();
|
||||
} else {
|
||||
currentCompetitorEdits.put(data.getKey(), data.getValue());
|
||||
}
|
||||
|
||||
final LeaderboardNameRaceColumnNameAndFleetName leaderboardNameRaceColumnNameAndFleetName =
|
||||
raceIdentifierToLeaderboardRaceColumnAndFleetMapper.getLeaderboardNameAndRaceColumnNameAndFleetName(raceIdentifier);
|
||||
if (leaderboardNameRaceColumnNameAndFleetName != null) {
|
||||
sailingService.getCompetitorRaceLogMarkPassingData(leaderboardNameRaceColumnNameAndFleetName.getLeaderboardName(),
|
||||
leaderboardNameRaceColumnNameAndFleetName.getRaceColumnName(),
|
||||
leaderboardNameRaceColumnNameAndFleetName.getFleetName(), competitor, new AsyncCallback<Map<Integer, Date>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorTryingToObtainRaceLogMarkPassingData(caught.getMessage()));
|
||||
}
|
||||
enableButtons();
|
||||
wayPointSelectionTable.redraw();
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public void onSuccess(Map<Integer, Date> result) {
|
||||
for (Entry<Integer, Date> data : result.entrySet()) {
|
||||
if (data.getValue() == null) {
|
||||
zeroBasedIndexOfFirstSuppressedWaypoint = data.getKey();
|
||||
} else {
|
||||
currentCompetitorEdits.put(data.getKey(), data.getValue());
|
||||
}
|
||||
}
|
||||
enableButtons();
|
||||
wayPointSelectionTable.redraw();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -384,7 +409,7 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
}
|
||||
|
||||
public void setLeaderboard(LeaderboardDTO leaderboard) {
|
||||
this.leaderboard = leaderboard;
|
||||
this.raceIdentifierToLeaderboardRaceColumnAndFleetMapper.setLeaderboard(leaderboard);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -429,23 +454,6 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
public void filteredCompetitorsListChanged(Iterable<CompetitorDTO> filteredCompetitors) {
|
||||
}
|
||||
|
||||
private String getLeaderboardName() {
|
||||
return leaderboard.name;
|
||||
}
|
||||
|
||||
private RaceColumnDTO getColumn() {
|
||||
RaceColumnDTO result = null;
|
||||
if (leaderboard != null) {
|
||||
for (RaceColumnDTO columnDTO : leaderboard.getRaceList()) {
|
||||
if (columnDTO.containsRace(raceIdentifier)) {
|
||||
result = columnDTO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractSettings getSettings() {
|
||||
return null;
|
||||
|
||||
+987
@@ -0,0 +1,987 @@
|
||||
package com.sap.sailing.gwt.ui.client.shared.charts;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.moxieapps.gwt.highcharts.client.Axis;
|
||||
import org.moxieapps.gwt.highcharts.client.BaseChart;
|
||||
import org.moxieapps.gwt.highcharts.client.Chart;
|
||||
import org.moxieapps.gwt.highcharts.client.ChartSubtitle;
|
||||
import org.moxieapps.gwt.highcharts.client.ChartTitle;
|
||||
import org.moxieapps.gwt.highcharts.client.Color;
|
||||
import org.moxieapps.gwt.highcharts.client.Credits;
|
||||
import org.moxieapps.gwt.highcharts.client.PlotLine.DashStyle;
|
||||
import org.moxieapps.gwt.highcharts.client.Point;
|
||||
import org.moxieapps.gwt.highcharts.client.Series;
|
||||
import org.moxieapps.gwt.highcharts.client.ToolTip;
|
||||
import org.moxieapps.gwt.highcharts.client.ToolTipData;
|
||||
import org.moxieapps.gwt.highcharts.client.ToolTipFormatter;
|
||||
import org.moxieapps.gwt.highcharts.client.XAxis;
|
||||
import org.moxieapps.gwt.highcharts.client.events.ChartClickEvent;
|
||||
import org.moxieapps.gwt.highcharts.client.events.ChartClickEventHandler;
|
||||
import org.moxieapps.gwt.highcharts.client.events.ChartSelectionEvent;
|
||||
import org.moxieapps.gwt.highcharts.client.events.ChartSelectionEventHandler;
|
||||
import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsData;
|
||||
import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsFormatter;
|
||||
import org.moxieapps.gwt.highcharts.client.labels.XAxisLabels;
|
||||
import org.moxieapps.gwt.highcharts.client.plotOptions.LinePlotOptions;
|
||||
import org.moxieapps.gwt.highcharts.client.plotOptions.Marker;
|
||||
|
||||
import com.google.gwt.ajaxloader.client.Properties.TypeException;
|
||||
import com.google.gwt.core.client.Callback;
|
||||
import com.google.gwt.core.client.JavaScriptObject;
|
||||
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
|
||||
import com.google.gwt.core.shared.GWT;
|
||||
import com.google.gwt.event.shared.HandlerRegistration;
|
||||
import com.google.gwt.i18n.client.NumberFormat;
|
||||
import com.google.gwt.maps.client.MapOptions;
|
||||
import com.google.gwt.maps.client.MapWidget;
|
||||
import com.google.gwt.maps.client.base.LatLng;
|
||||
import com.google.gwt.maps.client.base.LatLngBounds;
|
||||
import com.google.gwt.maps.client.controls.ControlPosition;
|
||||
import com.google.gwt.maps.client.events.click.ClickMapEvent;
|
||||
import com.google.gwt.maps.client.events.click.ClickMapHandler;
|
||||
import com.google.gwt.maps.client.events.mousedown.MouseDownMapEvent;
|
||||
import com.google.gwt.maps.client.events.mousedown.MouseDownMapHandler;
|
||||
import com.google.gwt.maps.client.events.mousemove.MouseMoveMapEvent;
|
||||
import com.google.gwt.maps.client.events.mousemove.MouseMoveMapHandler;
|
||||
import com.google.gwt.maps.client.events.mouseout.MouseOutMapEvent;
|
||||
import com.google.gwt.maps.client.events.mouseout.MouseOutMapHandler;
|
||||
import com.google.gwt.maps.client.events.mouseup.MouseUpMapEvent;
|
||||
import com.google.gwt.maps.client.events.mouseup.MouseUpMapHandler;
|
||||
import com.google.gwt.maps.client.events.resize.ResizeMapEvent;
|
||||
import com.google.gwt.maps.client.events.resize.ResizeMapHandler;
|
||||
import com.google.gwt.maps.client.events.rightclick.RightClickMapEvent;
|
||||
import com.google.gwt.maps.client.events.rightclick.RightClickMapHandler;
|
||||
import com.google.gwt.maps.client.mvc.MVCArray;
|
||||
import com.google.gwt.maps.client.overlays.Polyline;
|
||||
import com.google.gwt.maps.client.overlays.PolylineOptions;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.HTMLPanel;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.MenuBar;
|
||||
import com.google.gwt.user.client.ui.MenuItem;
|
||||
import com.google.gwt.user.client.ui.PopupPanel;
|
||||
import com.google.gwt.user.client.ui.RequiresResize;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.view.client.ListDataProvider;
|
||||
import com.google.gwt.view.client.SelectionChangeEvent;
|
||||
import com.sap.sailing.domain.common.FixType;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.MarkPositionService.MarkTrackDTO;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.MarkPositionService.MarkTracksDTO;
|
||||
import com.sap.sailing.gwt.ui.client.shared.racemap.BoundsUtil;
|
||||
import com.sap.sailing.gwt.ui.client.shared.racemap.CourseMarkOverlay;
|
||||
import com.sap.sailing.gwt.ui.client.shared.racemap.FixOverlay;
|
||||
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMap;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
|
||||
import com.sap.sailing.gwt.ui.raceboard.SideBySideComponentViewer;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTOWithSpeedWindTackAndLegType;
|
||||
import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.WindDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.racemap.CanvasOverlayV3;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.settings.AbstractSettings;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
|
||||
import com.sap.sse.gwt.client.player.TimeRangeWithZoomProvider;
|
||||
import com.sap.sse.gwt.client.player.Timer;
|
||||
import com.sap.sse.gwt.client.shared.components.Component;
|
||||
import com.sap.sse.gwt.client.shared.components.SettingsDialog;
|
||||
import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent;
|
||||
|
||||
public class EditMarkPositionPanel extends AbstractRaceChart implements Component<AbstractSettings>, RequiresResize, SelectionChangeEvent.Handler {
|
||||
protected static final int FIX_OVERLAY_Z_ORDER = 230;
|
||||
private final RaceMap raceMap;
|
||||
private final LeaderboardPanel leaderboardPanel;
|
||||
private final MarksPanel marksPanel;
|
||||
private Series markSeries;
|
||||
private LinePlotOptions markSeriesPlotOptions;
|
||||
private final Label noMarkSelectedLabel;
|
||||
private MapWidget map;
|
||||
private boolean visible;
|
||||
private List<HandlerRegistration> courseMarkClickHandlers;
|
||||
|
||||
private Map<MarkDTO, Pair<Date, Date>> marksFromToTimes;
|
||||
private Date raceFromTime;
|
||||
private Date raceToTime;
|
||||
|
||||
private Map<MarkDTO, SortedMap<GPSFixDTO, FixOverlay>> marks;
|
||||
private MarkDTO selectedMark;
|
||||
private Map<MarkDTO, Polyline> polylines;
|
||||
private final ListDataProvider<MarkDTO> markDataProvider;
|
||||
private SideBySideComponentViewer sideBySideComponentViewer;
|
||||
|
||||
private FixPositionChooser currentFixPositionChooser;
|
||||
private List<OverlayClickHandler> overlayClickHandlers;
|
||||
private final MarkPositionService markPositionService;
|
||||
|
||||
private final RaceIdentifierToLeaderboardRaceColumnAndFleetMapper raceIdentifierToLeaderboardRaceColumnAndFleetMapper;
|
||||
|
||||
public EditMarkPositionPanel(final RaceMap raceMap, final LeaderboardPanel leaderboardPanel,
|
||||
RegattaAndRaceIdentifier selectedRaceIdentifier, String leaderboardName, final StringMessages stringMessages,
|
||||
SailingServiceAsync sailingService, Timer timer, TimeRangeWithZoomProvider timeRangeWithZoomProvider,
|
||||
AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter) {
|
||||
super(sailingService, selectedRaceIdentifier, timer, timeRangeWithZoomProvider, stringMessages, asyncActionsExecutor, errorReporter);
|
||||
this.markPositionService = new MarkPositionServiceForSailingService(sailingService);
|
||||
this.raceIdentifierToLeaderboardRaceColumnAndFleetMapper = new RaceIdentifierToLeaderboardRaceColumnAndFleetMapper();
|
||||
this.raceMap = raceMap;
|
||||
this.leaderboardPanel = leaderboardPanel;
|
||||
this.polylines = new HashMap<>();
|
||||
this.markDataProvider = new ListDataProvider<>();
|
||||
this.marksPanel = new MarksPanel(this, markDataProvider, stringMessages);
|
||||
this.noMarkSelectedLabel = new Label(stringMessages.pleaseSelectAMark());
|
||||
this.noMarkSelectedLabel.setStyleName("abstractChartPanel-importantMessageOfChart");
|
||||
this.courseMarkClickHandlers = new ArrayList<>();
|
||||
this.marksFromToTimes = new HashMap<>();
|
||||
this.getEntryWidget().setTitle(stringMessages.editMarkPositions());
|
||||
this.setVisible(false);
|
||||
this.overlayClickHandlers = new ArrayList<>();
|
||||
chart = new Chart()
|
||||
.setPersistent(true)
|
||||
.setZoomType(BaseChart.ZoomType.X)
|
||||
.setMarginLeft(65)
|
||||
.setMarginRight(65)
|
||||
.setWidth100()
|
||||
.setHeight100()
|
||||
.setBorderWidth(0)
|
||||
.setBorderRadius(0)
|
||||
.setBackgroundColor(new Color("#FFFFFF"))
|
||||
.setPlotBackgroundColor("#f8f8f8")
|
||||
.setPlotBorderWidth(0)
|
||||
.setCredits(new Credits().setEnabled(false))
|
||||
.setChartTitle(new ChartTitle().setText(stringMessages.markFixes()))
|
||||
.setChartSubtitle(new ChartSubtitle().setText(stringMessages.clickAndDragToZoomIn()))
|
||||
.setPersistent(true);
|
||||
chart.setClickEventHandler(new ChartClickEventHandler() {
|
||||
@Override
|
||||
public boolean onClick(ChartClickEvent chartClickEvent) {
|
||||
return EditMarkPositionPanel.this.onClick(chartClickEvent);
|
||||
}
|
||||
});
|
||||
chart.setSelectionEventHandler(new ChartSelectionEventHandler() {
|
||||
@Override
|
||||
public boolean onSelection(ChartSelectionEvent chartSelectionEvent) {
|
||||
return EditMarkPositionPanel.this.onXAxisSelectionChange(chartSelectionEvent);
|
||||
}
|
||||
});
|
||||
chart.getXAxis().setType(Axis.Type.DATE_TIME)
|
||||
.setMaxZoom(60 * 1000) // 1 minute
|
||||
.setAxisTitleText(stringMessages.time());
|
||||
chart.getXAxis().setLabels(new XAxisLabels().setFormatter(new AxisLabelsFormatter() {
|
||||
@Override
|
||||
public String format(AxisLabelsData axisLabelsData) {
|
||||
return dateFormatHoursMinutes.format(new Date(axisLabelsData.getValueAsLong()));
|
||||
}
|
||||
}));
|
||||
chart.getYAxis().setAxisTitleText(stringMessages.distanceFromAveragePosition() + " (" + stringMessages.metersUnit() + ")").setOption("labels/enabled", false);
|
||||
timePlotLine = chart.getXAxis().createPlotLine().setColor("#656565").setWidth(1)
|
||||
.setDashStyle(DashStyle.SOLID);
|
||||
markSeriesPlotOptions = new LinePlotOptions().setSelected(true).setShowInLegend(false).setLineWidth(1).setColor("#000")
|
||||
.setHoverStateLineWidth(1).setEnableMouseTracking(true);
|
||||
markSeries = chart.createSeries().setType(Series.Type.LINE).setYAxis(0)
|
||||
.setPlotOptions(markSeriesPlotOptions).setName(stringMessages.distanceFromAveragePosition());
|
||||
chart.addSeries(markSeries, false, false);
|
||||
chart.setToolTip(new ToolTip().setEnabled(true).setFormatter(new ToolTipFormatter() {
|
||||
@Override
|
||||
public String format(ToolTipData toolTipData) {
|
||||
final String seriesName = toolTipData.getSeriesName();
|
||||
if (seriesName.equals(stringMessages.time())) {
|
||||
return "<b>" + seriesName + ":</b> " + dateFormat.format(new Date(toolTipData.getXAsLong()))
|
||||
+ "<br/>(" + stringMessages.clickChartToSetTime() + ")";
|
||||
} else {
|
||||
return "<b>" + (dateFormat.format(new Date(toolTipData.getXAsLong()))) + "</b>: "
|
||||
+ NumberFormat.getFormat("0.0").format(toolTipData.getYAsDouble()) + " m";
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private void canRemoveMarkFix(MarkDTO mark, GPSFixDTO fix, AsyncCallback<Boolean> callback) {
|
||||
markPositionService.canRemoveMarkFix(
|
||||
raceIdentifierToLeaderboardRaceColumnAndFleetMapper.getLeaderboardNameAndRaceColumnNameAndFleetName(selectedRaceIdentifier),
|
||||
mark, fix, callback);
|
||||
}
|
||||
|
||||
private static class Pixel extends JavaScriptObject {
|
||||
protected Pixel() {
|
||||
}
|
||||
|
||||
private native int getX() /*-{
|
||||
return this.x;
|
||||
}-*/;
|
||||
private native int getY() /*-{
|
||||
return this.y;
|
||||
}-*/;
|
||||
}
|
||||
|
||||
private class OverlayClickHandler {
|
||||
private final PopupPanel popup;
|
||||
private final MenuItem moveMenuItem;
|
||||
|
||||
private final FixOverlay overlay;
|
||||
private FixOverlay moveOverlay;
|
||||
|
||||
private final MouseDownMapHandler mouseDownHandler;
|
||||
private HandlerRegistration mouseDownHandlerRegistration;
|
||||
private final RightClickMapHandler rightClickHandler;
|
||||
private HandlerRegistration rightClickHandlerRegistration;
|
||||
|
||||
private int mouseDownX;
|
||||
private int mouseDownY;
|
||||
private boolean mouseDown;
|
||||
private boolean dragging;
|
||||
|
||||
//Currently the big problem with the OverlayClickHandler is that you cannot reliably detect if users use a touch device
|
||||
//and even if they don't at first they could switch from touch to mouse or the other way around. Thus, both input methods
|
||||
//are always present. Touch users should not accidentally drag fixes, because there is a buffer.
|
||||
public OverlayClickHandler(final MarkDTO mark, final GPSFixDTO fix, final FixOverlay overlay) {
|
||||
this.overlay = overlay;
|
||||
final Polyline polyline = polylines.get(mark);
|
||||
|
||||
map.addResizeHandler(new ResizeMapHandler() {
|
||||
@Override
|
||||
public void onEvent(ResizeMapEvent event) {
|
||||
onResizeMap(event);
|
||||
}
|
||||
});
|
||||
rightClickHandler = new RightClickMapHandler() {
|
||||
@Override
|
||||
public void onEvent(RightClickMapEvent event) {
|
||||
onRightClick(event);
|
||||
}
|
||||
};
|
||||
mouseDownHandler = new MouseDownMapHandler() {
|
||||
@Override
|
||||
public void onEvent(MouseDownMapEvent event) {
|
||||
try {
|
||||
if (event.getProperties().getNumber("which") == 1) {
|
||||
mouseDownX = event.getProperties().getNumber("pageX").intValue() - map.getAbsoluteLeft();
|
||||
mouseDownY = event.getProperties().getNumber("pageY").intValue() - map.getAbsoluteTop();
|
||||
mouseDown = true;
|
||||
dragging = false;
|
||||
MapOptions options = MapOptions.newInstance(false);
|
||||
options.setDraggable(false);
|
||||
map.setOptions(options);
|
||||
}
|
||||
} catch (TypeException e) {
|
||||
GWT.log("Exception trying to obtain pageX or pageY property on event "+event, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
map.addMouseMoveHandler(new MouseMoveMapHandler() {
|
||||
@Override
|
||||
public void onEvent(MouseMoveMapEvent event) {
|
||||
if (mouseDown) {
|
||||
int currentX = 0;
|
||||
int currentY = 0;
|
||||
try {
|
||||
Pixel pixel = event.getProperties().getObject("pixel").cast();
|
||||
currentX = pixel.getX();
|
||||
currentY = pixel.getY();
|
||||
} catch (TypeException e) {
|
||||
GWT.log("Exception trying to obtain pixel property on event "+event, e);
|
||||
}
|
||||
final int index = getIndexOfFixInPolyline(mark, fix);
|
||||
if (!dragging && Math.sqrt(Math.pow(currentX - mouseDownX, 2) +
|
||||
Math.pow(currentY - mouseDownY, 2)) > 15) {
|
||||
dragging = true;
|
||||
overlay.setVisible(false);
|
||||
setRedPoint(index);
|
||||
moveOverlay = new FixOverlay(map, overlay.getZIndex(), overlay.getGPSFixDTO(), overlay.getType(),
|
||||
"#f00", raceMap.getCoordinateSystem(), stringMessages.dragToChangePosition());
|
||||
moveOverlay.addMouseUpHandler(new MouseUpMapHandler() {
|
||||
@Override
|
||||
public void onEvent(MouseUpMapEvent event) {
|
||||
editMarkFix(mark, fix, fix.position);
|
||||
overlay.setVisible(true);
|
||||
resetPointColor(index);
|
||||
moveOverlay.removeFromMap();
|
||||
moveOverlay = null;
|
||||
|
||||
MapOptions options = MapOptions.newInstance(false);
|
||||
options.setDraggable(true);
|
||||
map.setOptions(options);
|
||||
mouseDown = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (dragging) {
|
||||
fix.position = raceMap.getCoordinateSystem().getPosition(event.getMouseEvent().getLatLng());
|
||||
overlay.setGPSFixDTO(fix);
|
||||
moveOverlay.setGPSFixDTO(fix);
|
||||
updateRedPoint(index);
|
||||
polyline.getPath().setAt(index, event.getMouseEvent().getLatLng());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
overlay.addMouseUpHandler(new MouseUpMapHandler() {
|
||||
@Override
|
||||
public void onEvent(MouseUpMapEvent event) {
|
||||
if (mouseDown) {
|
||||
if (!dragging) {
|
||||
onClick(event);
|
||||
}
|
||||
MapOptions options = MapOptions.newInstance(false);
|
||||
options.setDraggable(true);
|
||||
map.setOptions(options);
|
||||
mouseDown = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
map.addMouseOutMoveHandler(new MouseOutMapHandler() {
|
||||
@Override
|
||||
public void onEvent(MouseOutMapEvent event) {
|
||||
if (mouseDown) {
|
||||
if (dragging) {
|
||||
editMarkFix(mark, fix, fix.position);
|
||||
overlay.setVisible(true);
|
||||
resetPointColor(getIndexOfFixInPolyline(mark, fix));
|
||||
moveOverlay.removeFromMap();
|
||||
moveOverlay = null;
|
||||
}
|
||||
MapOptions options = MapOptions.newInstance(false);
|
||||
options.setDraggable(true);
|
||||
map.setOptions(options);
|
||||
mouseDown = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
popup = new PopupPanel(true);
|
||||
popup.setStyleName("EditMarkPositionPopup");
|
||||
final MenuBar menu = new MenuBar(true);
|
||||
moveMenuItem = new MenuItem(stringMessages.moveFix(), new ScheduledCommand() {
|
||||
@Override
|
||||
public void execute() {
|
||||
overlay.setVisible(false);
|
||||
if (currentFixPositionChooser == null) {
|
||||
OverlayClickHandler.this.unregisterAll();
|
||||
currentFixPositionChooser = new FixPositionChooser(EditMarkPositionPanel.this, stringMessages, map, getIndexOfFixInPolyline(mark, fix), polyline.getPath(), overlay, new Callback<Position, Exception>() {
|
||||
@Override
|
||||
public void onFailure(Exception reason) {
|
||||
overlay.setVisible(true);
|
||||
OverlayClickHandler.this.registerAll();
|
||||
resetCurrentFixPositionChooser();
|
||||
}
|
||||
@Override
|
||||
public void onSuccess(Position result) {
|
||||
editMarkFix(mark, fix, result);
|
||||
overlay.setVisible(true);
|
||||
OverlayClickHandler.this.registerAll();
|
||||
resetCurrentFixPositionChooser();
|
||||
}
|
||||
});
|
||||
}
|
||||
popup.hide();
|
||||
}
|
||||
});
|
||||
moveMenuItem.setTitle(stringMessages.useATouchOptimizedUI());
|
||||
menu.addItem(moveMenuItem);
|
||||
canRemoveMarkFix(mark, fix, new AsyncCallback<Boolean>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorCommunicatingWithServer()+": "+caught.getMessage(), /* silent */ true);
|
||||
addDeleteItemAndShowPopup(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
addDeleteItemAndShowPopup(result);
|
||||
}
|
||||
|
||||
private void addDeleteItemAndShowPopup(boolean enableDelete) {
|
||||
final MenuItem delete;
|
||||
if (enableDelete) {
|
||||
delete = new MenuItem(stringMessages.deleteFix(), new ScheduledCommand() {
|
||||
@Override
|
||||
public void execute() {
|
||||
removeMarkFix(mark, fix);
|
||||
popup.hide();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
delete = new MenuItem(stringMessages.deleteFix(), new ScheduledCommand() {
|
||||
@Override
|
||||
public void execute() {
|
||||
}
|
||||
});
|
||||
delete.setEnabled(false);
|
||||
delete.setTitle(stringMessages.theDeletionOfThisFix());
|
||||
}
|
||||
menu.addItem(delete);
|
||||
popup.setWidget(menu);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private int getIndexOfFixInPolyline(MarkDTO mark, GPSFixDTO fix) {
|
||||
int index;
|
||||
if (marks == null) {
|
||||
index = -1;
|
||||
} else {
|
||||
index = 0;
|
||||
for (GPSFixDTO fixToCompare : marks.get(mark).keySet()) {
|
||||
if (fixToCompare.timepoint.equals(fix.timepoint))
|
||||
return index;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
public void onClick(MouseUpMapEvent event) {
|
||||
setPopupPosition();
|
||||
moveMenuItem.setVisible(true);
|
||||
popup.show();
|
||||
}
|
||||
|
||||
public OverlayClickHandler register() {
|
||||
if (mouseDownHandlerRegistration == null) {
|
||||
mouseDownHandlerRegistration = overlay.addMouseDownHandler(mouseDownHandler);
|
||||
}
|
||||
if (rightClickHandlerRegistration == null) {
|
||||
rightClickHandlerRegistration = overlay.addRightClickHandler(rightClickHandler);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public OverlayClickHandler unregister() {
|
||||
if (mouseDownHandlerRegistration != null) {
|
||||
mouseDownHandlerRegistration.removeHandler();
|
||||
mouseDownHandlerRegistration = null;
|
||||
}
|
||||
if (rightClickHandlerRegistration != null) {
|
||||
rightClickHandlerRegistration.removeHandler();
|
||||
rightClickHandlerRegistration = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void unregisterAll() {
|
||||
for (OverlayClickHandler handler : overlayClickHandlers) {
|
||||
handler.unregister();
|
||||
}
|
||||
}
|
||||
|
||||
public void registerAll() {
|
||||
for (OverlayClickHandler handler : overlayClickHandlers) {
|
||||
handler.register();
|
||||
}
|
||||
}
|
||||
|
||||
private void setPopupPosition() {
|
||||
popup.setPopupPosition(overlay.getCanvas().getAbsoluteLeft() + (int) overlay.getFixScaleAndSize().getB().getWidth() / 2 + 5,
|
||||
overlay.getCanvas().getAbsoluteTop() + (int) overlay.getFixScaleAndSize().getB().getHeight() / 2 + 5);
|
||||
}
|
||||
|
||||
public void onResizeMap(ResizeMapEvent event) {
|
||||
if (popup.isVisible()) {
|
||||
popup.hide();
|
||||
}
|
||||
}
|
||||
|
||||
public void onRightClick(RightClickMapEvent event) {
|
||||
setPopupPosition();
|
||||
moveMenuItem.setVisible(false);
|
||||
popup.show();
|
||||
}
|
||||
}
|
||||
|
||||
public void setRedPoint(Point[] points, int index) {
|
||||
points[index].setMarker(new Marker().setFillColor("#f00"));
|
||||
}
|
||||
|
||||
public void setRedPoint(int index) {
|
||||
Point[] points = markSeries.getPoints();
|
||||
if (points.length > index) {
|
||||
setRedPoint(points, index);
|
||||
setSeriesPoints(markSeries, points);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateRedPoint(int index) {
|
||||
if (selectedMark != null && marks != null) {
|
||||
Point[] points = getSeriesPoints(marks.get(selectedMark).keySet());
|
||||
if (points.length > index) {
|
||||
setRedPoint(points, index);
|
||||
setSeriesPoints(markSeries, points);
|
||||
chart.redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void resetPointColor(int index) {
|
||||
Point[] points = markSeries.getPoints();
|
||||
if (points.length > index) {
|
||||
points[index].setMarker(new Marker().setFillColor(selectedMark.color));
|
||||
setSeriesPoints(markSeries, points);
|
||||
}
|
||||
}
|
||||
|
||||
private void finalizeLoadingData() {
|
||||
hideLoading();
|
||||
onSelectionChange(null);
|
||||
onResize();
|
||||
}
|
||||
|
||||
private void loadData(final Date from, final Date to) {
|
||||
if (selectedRaceIdentifier != null && from != null && to != null && marks == null) {
|
||||
setWidget(chart);
|
||||
showLoading(stringMessages.loadingMarkFixes());
|
||||
markPositionService.getMarkTracks(
|
||||
raceIdentifierToLeaderboardRaceColumnAndFleetMapper.getLeaderboardNameAndRaceColumnNameAndFleetName(selectedRaceIdentifier),
|
||||
new AsyncCallback<MarkTracksDTO>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
finalizeLoadingData();
|
||||
errorReporter.reportError(stringMessages.errorCommunicatingWithServer()+": "+caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(MarkTracksDTO markTracks) {
|
||||
Map<MarkDTO, Iterable<GPSFixDTO>> result = new HashMap<>();
|
||||
for (MarkTrackDTO track : markTracks.getTracks()) {
|
||||
result.put(track.getMark(), track.getFixes());
|
||||
marks = new HashMap<MarkDTO, SortedMap<GPSFixDTO, FixOverlay>>();
|
||||
raceFromTime = timeRangeWithZoomProvider.getFromTime();
|
||||
raceToTime = timeRangeWithZoomProvider.getToTime();
|
||||
for (final Map.Entry<MarkDTO, Iterable<GPSFixDTO>> fixes : result.entrySet()) {
|
||||
Date fromTime = raceFromTime;
|
||||
Date toTime = raceToTime;
|
||||
PolylineOptions options = PolylineOptions.newInstance();
|
||||
if (map != null) {
|
||||
options.setMap(map);
|
||||
}
|
||||
options.setStrokeWeight(1);
|
||||
options.setVisible(false);
|
||||
final Polyline polyline = Polyline.newInstance(options); // Line can not be dashed at the moment, because line symbols are not supported
|
||||
polylines.put(fixes.getKey(), polyline);
|
||||
SortedMap<GPSFixDTO, FixOverlay> fixOverlayMap = new TreeMap<>(new Comparator<GPSFixDTO>() {
|
||||
@Override
|
||||
public int compare(GPSFixDTO o1, GPSFixDTO o2) {
|
||||
return o1.timepoint.compareTo(o2.timepoint);
|
||||
}
|
||||
});
|
||||
for (final GPSFixDTO fix : fixes.getValue()) {
|
||||
final FixOverlay overlay = new FixOverlay(map, FIX_OVERLAY_Z_ORDER, fix, FixType.BUOY, fixes.getKey().color, raceMap.getCoordinateSystem(), stringMessages.dragToChangePosition());
|
||||
fixOverlayMap.put(fix, overlay);
|
||||
overlay.setVisible(false);
|
||||
overlayClickHandlers.add(new OverlayClickHandler(fixes.getKey(), fix, overlay).register());
|
||||
if (fromTime.after(fix.timepoint)) {
|
||||
fromTime = fix.timepoint;
|
||||
} else if (toTime.before(fix.timepoint)) {
|
||||
toTime = fix.timepoint;
|
||||
}
|
||||
}
|
||||
marks.put(fixes.getKey(), fixOverlayMap);
|
||||
updatePolylinePoints(fixes.getKey());
|
||||
marksFromToTimes.put(fixes.getKey(), new Pair<Date, Date>(fromTime, toTime));
|
||||
}
|
||||
List<MarkDTO> markList = new ArrayList<MarkDTO>();
|
||||
markList.addAll(marks.keySet());
|
||||
markDataProvider.setList(markList);
|
||||
}
|
||||
finalizeLoadingData();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void addMarkFix(final MarkDTO mark, final Date timepoint, final Position fixPosition) {
|
||||
final GPSFixDTOWithSpeedWindTackAndLegType fix = new GPSFixDTOWithSpeedWindTackAndLegType(timepoint, fixPosition, null, new WindDTO(), null, null, false);
|
||||
markPositionService.addMarkFix(
|
||||
raceIdentifierToLeaderboardRaceColumnAndFleetMapper.getLeaderboardNameAndRaceColumnNameAndFleetName(selectedRaceIdentifier),
|
||||
mark, fix, new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorCommunicatingWithServer()+": "+caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
FixOverlay overlay = new FixOverlay(map, FIX_OVERLAY_Z_ORDER, fix, FixType.BUOY, mark.color, raceMap.getCoordinateSystem(), stringMessages.dragToChangePosition());
|
||||
overlayClickHandlers.add(new OverlayClickHandler(mark, fix, overlay).register());
|
||||
marks.get(mark).put(fix, overlay);
|
||||
updatePolylinePoints(mark);
|
||||
setSeriesPoints(mark);
|
||||
onResize();
|
||||
showNotification(stringMessages.fixSuccessfullyAdded(), NotificationType.SUCCESS);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void editMarkFix(final MarkDTO mark, final GPSFixDTO fix, final Position newPosition) {
|
||||
markPositionService.editMarkFix(
|
||||
raceIdentifierToLeaderboardRaceColumnAndFleetMapper.getLeaderboardNameAndRaceColumnNameAndFleetName(selectedRaceIdentifier),
|
||||
mark, fix, newPosition, new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorCommunicatingWithServer()+": "+caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
fix.position = newPosition;
|
||||
marks.get(mark).get(fix).setGPSFixDTO(fix);
|
||||
updatePolylinePoints(mark);
|
||||
setSeriesPoints(mark);
|
||||
onResize();
|
||||
showNotification(stringMessages.fixPositionSuccessfullyEdited(), NotificationType.SUCCESS);
|
||||
timer.setTime(timer.getTime().getTime()+1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeMarkFix(final MarkDTO mark, final GPSFixDTO fix) {
|
||||
markPositionService.removeMarkFix(
|
||||
raceIdentifierToLeaderboardRaceColumnAndFleetMapper.getLeaderboardNameAndRaceColumnNameAndFleetName(selectedRaceIdentifier),
|
||||
mark, fix, new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorCommunicatingWithServer()+": "+caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
CanvasOverlayV3 overlay = marks.get(mark).remove(fix);
|
||||
overlay.removeFromMap();
|
||||
updatePolylinePoints(mark);
|
||||
setSeriesPoints(mark);
|
||||
onResize();
|
||||
showNotification(stringMessages.fixSuccessfullyRemoved(), NotificationType.SUCCESS);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updatePolylinePoints(MarkDTO mark) {
|
||||
MVCArray<LatLng> path = polylines.get(mark).getPath();
|
||||
path.clear();
|
||||
for (GPSFixDTO fix : marks.get(mark).keySet()) {
|
||||
path.push(LatLng.newInstance(fix.position.getLatDeg(), fix.position.getLngDeg()));
|
||||
}
|
||||
}
|
||||
|
||||
public Point[] getSeriesPoints(Collection<GPSFixDTO> fixes) {
|
||||
double latAverage = 0;
|
||||
double lngAverage = 0;
|
||||
for (GPSFixDTO fix : fixes) {
|
||||
latAverage += fix.position.getLatDeg();
|
||||
lngAverage += fix.position.getLngDeg();
|
||||
}
|
||||
latAverage /= fixes.size();
|
||||
lngAverage /= fixes.size();
|
||||
Position averagePosition = new DegreePosition(latAverage, lngAverage);
|
||||
|
||||
Point[] points = new Point[fixes.size()];
|
||||
int i = 0;
|
||||
for (GPSFixDTO fix : fixes) {
|
||||
final double metersFromAverage = fix.position.getDistance(averagePosition).getMeters();
|
||||
points[i] = new Point(fix.timepoint.getTime(), metersFromAverage);
|
||||
i++;
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
private void setSeriesPoints(MarkDTO mark) {
|
||||
setSeriesPoints(markSeries, getSeriesPoints(marks.get(mark).keySet()));
|
||||
}
|
||||
|
||||
public void setSeriesPoints(Point[] points) {
|
||||
setSeriesPoints(markSeries, points);
|
||||
}
|
||||
|
||||
public void setVisible(boolean visible) {
|
||||
this.visible = visible;
|
||||
if (map == null) {
|
||||
map = raceMap.getMap();
|
||||
if (map != null) {
|
||||
setMap(map);
|
||||
}
|
||||
}
|
||||
if (this.visible) {
|
||||
if (sideBySideComponentViewer != null) {
|
||||
sideBySideComponentViewer.setLeftComponent(marksPanel);
|
||||
sideBySideComponentViewer.setLeftComponentToggleButtonVisible(false);
|
||||
}
|
||||
for (final CourseMarkOverlay overlay : raceMap.getCourseMarkOverlays().values()) {
|
||||
courseMarkClickHandlers.add(overlay.addClickHandler(new ClickMapHandler() {
|
||||
@Override
|
||||
public void onEvent(ClickMapEvent event) {
|
||||
marksPanel.select(overlay.getMark());
|
||||
}
|
||||
}));
|
||||
}
|
||||
raceMap.unregisterAllCourseMarkInfoWindowClickHandlers();
|
||||
} else {
|
||||
if (currentFixPositionChooser != null) {
|
||||
currentFixPositionChooser.cancel();
|
||||
currentFixPositionChooser = null;
|
||||
}
|
||||
marksPanel.deselectMark();
|
||||
selectedMark = null;
|
||||
if (sideBySideComponentViewer != null) {
|
||||
sideBySideComponentViewer.setLeftComponent(leaderboardPanel);
|
||||
sideBySideComponentViewer.setLeftComponentToggleButtonVisible(true);
|
||||
}
|
||||
for (HandlerRegistration registration : courseMarkClickHandlers) {
|
||||
registration.removeHandler();
|
||||
}
|
||||
raceMap.unregisterAllCourseMarkInfoWindowClickHandlers();
|
||||
raceMap.registerAllCourseMarkInfoWindowClickHandlers();
|
||||
}
|
||||
super.setVisible(this.visible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResize() {
|
||||
if (selectedMark != null) {
|
||||
LatLngBounds bounds = null;
|
||||
if (marks != null) {
|
||||
for (GPSFixDTO fix : marks.get(selectedMark).keySet()) {
|
||||
if (bounds == null) {
|
||||
bounds = BoundsUtil.getAsBounds(raceMap.getCoordinateSystem().toLatLng(fix.position));
|
||||
} else {
|
||||
bounds = bounds.extend(raceMap.getCoordinateSystem().toLatLng(fix.position));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
chart.setSizeToMatchContainer();
|
||||
chart.redraw();
|
||||
}
|
||||
|
||||
public void redrawChart() {
|
||||
chart.redraw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocalizedShortName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Widget getEntryWidget() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSettings() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettingsDialogComponent<AbstractSettings> getSettingsDialogComponent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSettings(AbstractSettings newSettings) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDependentCssClassName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setMap(final MapWidget map) {
|
||||
this.map = map;
|
||||
for (Map.Entry<MarkDTO, Polyline> polyline : polylines.entrySet()) {
|
||||
polyline.getValue().setMap(map);
|
||||
}
|
||||
}
|
||||
|
||||
public Date getTimepoint() {
|
||||
return timer.getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Button createSettingsButton() {
|
||||
Button settingsButton = SettingsDialog.createSettingsButton(this, stringMessages);
|
||||
settingsButton.setVisible(false);
|
||||
return settingsButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void timeChanged(Date newTime, Date oldTime) {
|
||||
if (isVisible()) {
|
||||
loadData(timeRangeWithZoomProvider.getFromTime(), timeRangeWithZoomProvider.getToTime());
|
||||
updateTimePlotLine(newTime);
|
||||
}
|
||||
}
|
||||
|
||||
public void setComponentViewer(SideBySideComponentViewer sideBySideComponentViewer) {
|
||||
this.sideBySideComponentViewer = sideBySideComponentViewer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionChange(SelectionChangeEvent event) {
|
||||
selectedMark = marksPanel.getSelectedMark();
|
||||
if (currentFixPositionChooser != null) {
|
||||
currentFixPositionChooser.cancel();
|
||||
currentFixPositionChooser = null;
|
||||
}
|
||||
if (selectedMark != null) {
|
||||
if (marksFromToTimes.get(selectedMark) != null) {
|
||||
// For some reason the time slider does not change with this method only if you comment out line 430 and 432 in TimePanel it works
|
||||
timeRangeWithZoomProvider.setTimeRange(marksFromToTimes.get(selectedMark).getA(), marksFromToTimes.get(selectedMark).getB());
|
||||
}
|
||||
setWidget(chart);
|
||||
markSeries.remove();
|
||||
markSeries.setPlotOptions(markSeriesPlotOptions.setMarker(
|
||||
new Marker().setFillColor(selectedMark.color != null ? selectedMark.color : "#efab00")
|
||||
.setLineColor("#fff").setLineWidth(2)));
|
||||
chart.addSeries(markSeries);
|
||||
setSeriesPoints(selectedMark);
|
||||
onResize(); // redraw chart
|
||||
hideAllCourseMarkOverlaysExceptSelected();
|
||||
raceMap.hideAllHelplines();
|
||||
if (marks != null) {
|
||||
for (final Entry<MarkDTO, SortedMap<GPSFixDTO, FixOverlay>> e : marks.entrySet()) {
|
||||
for (CanvasOverlayV3 overlay : e.getValue().values()) {
|
||||
// show only tail of selected mark, hide all others
|
||||
overlay.setVisible(selectedMark.equals(e.getKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (final Entry<MarkDTO, Polyline> e : polylines.entrySet()) {
|
||||
e.getValue().setVisible(selectedMark.equals(e.getKey()));
|
||||
}
|
||||
} else {
|
||||
if (raceFromTime != null && raceToTime != null) {
|
||||
timeRangeWithZoomProvider.setTimeRange(raceFromTime, raceToTime);
|
||||
}
|
||||
setWidget(noMarkSelectedLabel);
|
||||
showAllCourseMarkOverlays();
|
||||
raceMap.showAllHelplinesToShow();
|
||||
hideAllFixOverlays();
|
||||
hideAllPolylines();
|
||||
}
|
||||
}
|
||||
|
||||
public void showAllCourseMarkOverlays() {
|
||||
for (Map.Entry<String, CourseMarkOverlay> overlay : raceMap.getCourseMarkOverlays().entrySet()) {
|
||||
overlay.getValue().setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void hideAllCourseMarkOverlaysExceptSelected() {
|
||||
for (Map.Entry<String, CourseMarkOverlay> overlay : raceMap.getCourseMarkOverlays().entrySet()) {
|
||||
overlay.getValue().setVisible(overlay.getKey().equals(selectedMark.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
public void hideAllFixOverlays() {
|
||||
if (marks != null) {
|
||||
for (Map.Entry<MarkDTO, SortedMap<GPSFixDTO, FixOverlay>> mark : marks.entrySet()) {
|
||||
for (CanvasOverlayV3 overlay : mark.getValue().values()) {
|
||||
overlay.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void hideAllPolylines() {
|
||||
for (Map.Entry<MarkDTO, Polyline> polyline : polylines.entrySet()) {
|
||||
polyline.getValue().setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call {@link #resetCurrentFixPositionChooser()} after the fix position chooser is done.
|
||||
*
|
||||
* @throws MultipleFixPositionChooserException
|
||||
* Is thrown when another fix position chooser is still open
|
||||
*/
|
||||
public void createFixPositionChooserToAddFixToMark(MarkDTO mark, Callback<Position, Exception> callback) {
|
||||
if (currentFixPositionChooser != null) {
|
||||
return;
|
||||
}
|
||||
int index = 0;
|
||||
for (GPSFixDTO fix : marks.get(mark).keySet()) {
|
||||
if (fix.timepoint.after(timer.getTime()))
|
||||
break;
|
||||
index++;
|
||||
}
|
||||
Polyline polyline = polylines.get(mark);
|
||||
currentFixPositionChooser = new FixPositionChooser(this, stringMessages, map, index, polyline != null ? polyline.getPath() : null, map.getCenter(), raceMap.getCoordinateSystem(), callback);
|
||||
}
|
||||
|
||||
public void resetCurrentFixPositionChooser() {
|
||||
currentFixPositionChooser = null;
|
||||
}
|
||||
|
||||
private com.google.gwt.user.client.Timer notificationTimer;
|
||||
|
||||
public enum NotificationType {
|
||||
INFO,
|
||||
ERROR,
|
||||
SUCCESS
|
||||
}
|
||||
|
||||
public void showNotification(String message) {
|
||||
showNotification(message, NotificationType.INFO);
|
||||
}
|
||||
|
||||
public void showNotification(String message, NotificationType type) {
|
||||
if (notificationTimer != null && notificationTimer.isRunning()) {
|
||||
notificationTimer.run();
|
||||
}
|
||||
final HTMLPanel text = new HTMLPanel("<div style = \" color: " + (type == NotificationType.ERROR ? "#c00" : type == NotificationType.SUCCESS ? "#0c0" : "#000") + "\">" + message + "</div>");
|
||||
text.setStyleName("EditMarkPositionNotification");
|
||||
map.setControls(ControlPosition.TOP_CENTER, text);
|
||||
|
||||
notificationTimer = new com.google.gwt.user.client.Timer(){
|
||||
@Override
|
||||
public void run() {
|
||||
text.removeFromParent();
|
||||
}
|
||||
};
|
||||
notificationTimer.schedule(5000);
|
||||
}
|
||||
|
||||
public boolean hasFixAtTimePoint(MarkDTO mark, Date timePoint) {
|
||||
boolean result = false;
|
||||
for (GPSFixDTO fix : marks.get(mark).keySet()) {
|
||||
if (fix.timepoint.equals(timePoint)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<GPSFixDTO> getMarkFixes() {
|
||||
if (selectedMark != null) {
|
||||
List<GPSFixDTO> set = new ArrayList<>();
|
||||
set.addAll(marks.get(selectedMark).keySet());
|
||||
return set;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public XAxis getXAxis() {
|
||||
return chart.getXAxis();
|
||||
}
|
||||
|
||||
public void setLeaderboard(LeaderboardDTO leaderboard) {
|
||||
raceIdentifierToLeaderboardRaceColumnAndFleetMapper.setLeaderboard(leaderboard);
|
||||
}
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
package com.sap.sailing.gwt.ui.client.shared.charts;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.moxieapps.gwt.highcharts.client.Color;
|
||||
import org.moxieapps.gwt.highcharts.client.PlotLine;
|
||||
import org.moxieapps.gwt.highcharts.client.Point;
|
||||
import org.moxieapps.gwt.highcharts.client.PlotLine.DashStyle;
|
||||
|
||||
import com.google.gwt.core.client.Callback;
|
||||
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
|
||||
import com.google.gwt.event.shared.HandlerRegistration;
|
||||
import com.google.gwt.maps.client.MapWidget;
|
||||
import com.google.gwt.maps.client.base.LatLng;
|
||||
import com.google.gwt.maps.client.controls.ControlPosition;
|
||||
import com.google.gwt.maps.client.events.center.CenterChangeMapEvent;
|
||||
import com.google.gwt.maps.client.events.center.CenterChangeMapHandler;
|
||||
import com.google.gwt.maps.client.mvc.MVCArray;
|
||||
import com.google.gwt.user.client.ui.MenuBar;
|
||||
import com.google.gwt.user.client.ui.MenuItem;
|
||||
import com.sap.sailing.domain.common.FixType;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.shared.racemap.CoordinateSystem;
|
||||
import com.sap.sailing.gwt.ui.client.shared.racemap.FixOverlay;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTOWithSpeedWindTackAndLegType;
|
||||
import com.sap.sailing.gwt.ui.shared.WindDTO;
|
||||
|
||||
public class FixPositionChooser {
|
||||
private final Callback<Position, Exception> callback;
|
||||
private final MapWidget map;
|
||||
private final boolean newFix;
|
||||
private final int polylineFixIndex;
|
||||
private final MVCArray<LatLng> polylinePath;
|
||||
private final LatLng startPos;
|
||||
private final FixOverlay overlay;
|
||||
private final CoordinateSystem coordinateSystem;
|
||||
private FixOverlay moveOverlay;
|
||||
private HandlerRegistration centerChangeHandlerRegistration;
|
||||
private MenuBar menu;
|
||||
private final EditMarkPositionPanel editMarkPositionPanel;
|
||||
private final StringMessages stringMessages;
|
||||
private final PlotLine redTimeLine;
|
||||
|
||||
/**
|
||||
* Use this constructor when there is already a fix with an overlay.
|
||||
* This constructor will automatically assume that an existing fix is moved.
|
||||
* @param editMarkPositionPanel
|
||||
* @param map
|
||||
* @param polylineFixIndex Position of the fix in the polyline path
|
||||
* @param polylinePath
|
||||
* @param overlay
|
||||
* @param callback
|
||||
*/
|
||||
public FixPositionChooser(final EditMarkPositionPanel editMarkPositionPanel, final StringMessages stringMessages, final MapWidget map, final int polylineFixIndex, final MVCArray<LatLng> polylinePath, final FixOverlay overlay,
|
||||
final Callback<Position, Exception> callback) {
|
||||
this(editMarkPositionPanel, stringMessages, false, map, polylineFixIndex, polylinePath, overlay, overlay.getLatLngPosition(), overlay.getCoordinateSystem(),
|
||||
stringMessages.confirmMove(), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this constructor when you want to add a fix and not move one.
|
||||
* This constructor will automatically assume that there is no existing fix.
|
||||
*/
|
||||
public FixPositionChooser(final EditMarkPositionPanel editMarkPositionPanel, final StringMessages stringMessages, final MapWidget map, final int polylineFixIndex, final MVCArray<LatLng> polylinePath, final LatLng startPos,
|
||||
final CoordinateSystem coordinateSystem, final Callback<Position, Exception> callback) {
|
||||
this(editMarkPositionPanel, stringMessages, true, map, polylineFixIndex, polylinePath, null, startPos, coordinateSystem,
|
||||
stringMessages.confirmNewFix(), callback);
|
||||
}
|
||||
|
||||
// The problem of not knowing if it is a touchscreen or mouse the user is currently using, applies to this class too.
|
||||
// The touch optimized input is currently implemented, because mouse users can use it more easily than the other way around.
|
||||
private FixPositionChooser(final EditMarkPositionPanel editMarkPositionPanel, final StringMessages stringMessages, final boolean newFix, final MapWidget map, final int polylineFixIndex, final MVCArray<LatLng> polylinePath,
|
||||
final FixOverlay overlay, final LatLng startPos, final CoordinateSystem coordinateSystem, final String confirmButtonText,
|
||||
final Callback<Position, Exception> callback) {
|
||||
this.callback = callback;
|
||||
this.stringMessages = stringMessages;
|
||||
this.map = map;
|
||||
this.newFix = newFix;
|
||||
this.polylineFixIndex = polylineFixIndex;
|
||||
this.polylinePath = polylinePath;
|
||||
this.overlay = overlay;
|
||||
this.startPos = startPos;
|
||||
this.coordinateSystem = coordinateSystem;
|
||||
this.editMarkPositionPanel = editMarkPositionPanel;
|
||||
this.editMarkPositionPanel.showNotification(stringMessages.selectAFixPositionBy());
|
||||
this.redTimeLine = editMarkPositionPanel.getXAxis().createPlotLine().setColor(new Color(255, 0, 0)).setWidth(1.5).setDashStyle(DashStyle.SOLID);
|
||||
setupUIOverlay(confirmButtonText);
|
||||
}
|
||||
|
||||
private void setupUIOverlay(final String confirmButtonText) {
|
||||
final GPSFixDTO fix;
|
||||
if (overlay != null) {
|
||||
final GPSFixDTO oldFix = overlay.getGPSFixDTO();
|
||||
fix = new GPSFixDTO(oldFix.timepoint, oldFix.position);
|
||||
this.moveOverlay = new FixOverlay(map, overlay.getZIndex(), fix, overlay.getType(), "#f00", coordinateSystem, stringMessages.dragToChangePosition());
|
||||
} else {
|
||||
fix = new GPSFixDTOWithSpeedWindTackAndLegType(editMarkPositionPanel.getTimepoint(), coordinateSystem.getPosition(startPos), null, new WindDTO(), null, null, false);
|
||||
this.moveOverlay = new FixOverlay(map, EditMarkPositionPanel.FIX_OVERLAY_Z_ORDER+1, fix, FixType.BUOY, "#f00", coordinateSystem, stringMessages.dragToChangePosition());
|
||||
}
|
||||
redTimeLine.setValue(fix.timepoint.getTime());
|
||||
editMarkPositionPanel.getXAxis().addPlotLines(redTimeLine);
|
||||
map.panTo(startPos);
|
||||
if (polylinePath != null && newFix) {
|
||||
polylinePath.insertAt(polylineFixIndex, map.getCenter());
|
||||
}
|
||||
setRedPointInChart(fix);
|
||||
centerChangeHandlerRegistration = map.addCenterChangeHandler(new CenterChangeMapHandler() {
|
||||
@Override
|
||||
public void onEvent(CenterChangeMapEvent event) {
|
||||
fix.position = coordinateSystem.getPosition(map.getCenter());
|
||||
moveOverlay.setGPSFixDTO(fix);
|
||||
if (polylinePath != null) {
|
||||
polylinePath.setAt(polylineFixIndex, map.getCenter());
|
||||
}
|
||||
setRedPointInChart(fix);
|
||||
}
|
||||
});
|
||||
menu = new MenuBar(/* vertical */ false);
|
||||
menu.setStyleName("EditMarkPositionConfirmCancelButtons");
|
||||
MenuItem confirm = new MenuItem(confirmButtonText, new ScheduledCommand() {
|
||||
@Override
|
||||
public void execute() {
|
||||
destroyUIOverlay();
|
||||
cleanupChart();
|
||||
callback.onSuccess(coordinateSystem.getPosition(map.getCenter()));
|
||||
}
|
||||
});
|
||||
MenuItem cancel = new MenuItem(stringMessages.cancel(), new ScheduledCommand() {
|
||||
@Override
|
||||
public void execute() {
|
||||
cancel();
|
||||
}
|
||||
});
|
||||
menu.addItem(confirm);
|
||||
menu.addItem(cancel);
|
||||
map.setControls(ControlPosition.BOTTOM_LEFT, menu);
|
||||
}
|
||||
|
||||
private void destroyUIOverlay() {
|
||||
moveOverlay.removeFromMap();
|
||||
centerChangeHandlerRegistration.removeHandler();
|
||||
menu.removeFromParent();
|
||||
}
|
||||
|
||||
private void cleanupChart() {
|
||||
editMarkPositionPanel.getXAxis().removePlotLine(redTimeLine);
|
||||
editMarkPositionPanel.updateRedPoint(polylineFixIndex);
|
||||
editMarkPositionPanel.resetPointColor(polylineFixIndex);
|
||||
editMarkPositionPanel.redrawChart();
|
||||
}
|
||||
|
||||
private void setRedPointInChart(GPSFixDTO fix) {
|
||||
List<GPSFixDTO> fixes = editMarkPositionPanel.getMarkFixes();
|
||||
if (fixes != null) {
|
||||
if (newFix) {
|
||||
fixes.add(polylineFixIndex, fix);
|
||||
} else {
|
||||
fixes.set(polylineFixIndex, fix);
|
||||
}
|
||||
Point[] points = editMarkPositionPanel.getSeriesPoints(fixes);
|
||||
editMarkPositionPanel.setRedPoint(points, polylineFixIndex);
|
||||
editMarkPositionPanel.setSeriesPoints(points);
|
||||
editMarkPositionPanel.redrawChart();
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
destroyUIOverlay();
|
||||
cleanupChart();
|
||||
if (polylinePath != null) {
|
||||
if (newFix) {
|
||||
polylinePath.removeAt(polylineFixIndex);
|
||||
} else {
|
||||
polylinePath.setAt(polylineFixIndex, overlay.getLatLngPosition());
|
||||
}
|
||||
}
|
||||
callback.onFailure(null);
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.sap.sailing.gwt.ui.client.shared.charts;
|
||||
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.RaceIdentifierToLeaderboardRaceColumnAndFleetMapper.LeaderboardNameRaceColumnNameAndFleetName;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
|
||||
public interface MarkPositionService {
|
||||
public static class MarkTrackDTO implements IsSerializable {
|
||||
private MarkDTO mark;
|
||||
private Iterable<GPSFixDTO> fixes;
|
||||
private boolean thinnedOut;
|
||||
|
||||
MarkTrackDTO() {} // for GWT serialization
|
||||
|
||||
public MarkTrackDTO(MarkDTO mark, Iterable<GPSFixDTO> fixes, boolean thinnedOut) {
|
||||
this.mark = mark;
|
||||
this.fixes = fixes;
|
||||
this.thinnedOut = thinnedOut;
|
||||
}
|
||||
|
||||
public MarkDTO getMark() {
|
||||
return mark;
|
||||
}
|
||||
|
||||
public Iterable<GPSFixDTO> getFixes() {
|
||||
return fixes;
|
||||
}
|
||||
|
||||
public boolean isThinnedOut() {
|
||||
return thinnedOut;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MarkTracksDTO implements IsSerializable {
|
||||
private Iterable<MarkTrackDTO> tracks;
|
||||
|
||||
MarkTracksDTO() {} // for GWT serialization
|
||||
|
||||
public MarkTracksDTO(Iterable<MarkTrackDTO> tracks) {
|
||||
this.tracks = tracks;
|
||||
}
|
||||
|
||||
public Iterable<MarkTrackDTO> getTracks() {
|
||||
return tracks;
|
||||
}
|
||||
}
|
||||
|
||||
// Jonas Dann: I had no great idea for handling of massive amount of fixes. An exception is no real option. I
|
||||
// think the fixes have to be thinned out somehow and maybe shown in more detail when zoomed in. The aggregation of
|
||||
// fixes is in my eyes no option, because then you have to define behaviour when moving them.
|
||||
// I had no remaining time to incorporate the thinned out variable in my code.
|
||||
void getMarkTracks(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, AsyncCallback<MarkTracksDTO> callback);
|
||||
|
||||
/**
|
||||
* The service may decide whether a mark fix can be removed. It may, for example, be impossible to
|
||||
* cleanly remove a mark fix if a tracked race already exists and the mark fixes are already part of
|
||||
* the GPS fix track which currently does not support a remove operation. However, when only the
|
||||
* regatta log is the basis of the service and no tracked race exists yet, mark fixes may be removed
|
||||
* by revoking the device mappings.
|
||||
*/
|
||||
void canRemoveMarkFix(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, MarkDTO mark, GPSFixDTO fix, AsyncCallback<Boolean> callback);
|
||||
|
||||
void removeMarkFix(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, MarkDTO mark, GPSFixDTO fix, AsyncCallback<Void> callback);
|
||||
|
||||
void addMarkFix(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, MarkDTO mark, GPSFixDTO newFix, AsyncCallback<Void> callback);
|
||||
|
||||
void editMarkFix(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, MarkDTO mark, GPSFixDTO oldFix, Position newPosition, AsyncCallback<Void> callback);
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.sap.sailing.gwt.ui.client.shared.charts;
|
||||
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.RaceIdentifierToLeaderboardRaceColumnAndFleetMapper.LeaderboardNameRaceColumnNameAndFleetName;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
|
||||
public class MarkPositionServiceForSailingService implements MarkPositionService {
|
||||
private final SailingServiceAsync sailingService;
|
||||
|
||||
public MarkPositionServiceForSailingService(SailingServiceAsync sailingService) {
|
||||
super();
|
||||
this.sailingService = sailingService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getMarkTracks(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, AsyncCallback<MarkTracksDTO> callback) {
|
||||
sailingService.getMarkTracks(raceIdentifier.getLeaderboardName(), raceIdentifier.getRaceColumnName(), raceIdentifier.getFleetName(), callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void canRemoveMarkFix(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, MarkDTO mark,
|
||||
GPSFixDTO fix, AsyncCallback<Boolean> callback) {
|
||||
sailingService.canRemoveMarkFix(raceIdentifier.getLeaderboardName(), raceIdentifier.getRaceColumnName(), raceIdentifier.getFleetName(), mark.getIdAsString(), fix, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMarkFix(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, MarkDTO mark, GPSFixDTO fix, AsyncCallback<Void> callback) {
|
||||
sailingService.removeMarkFix(raceIdentifier.getLeaderboardName(), raceIdentifier.getRaceColumnName(), raceIdentifier.getFleetName(), mark.getIdAsString(), fix, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMarkFix(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, MarkDTO mark, GPSFixDTO newFix, AsyncCallback<Void> callback) {
|
||||
sailingService.addMarkFix(raceIdentifier.getLeaderboardName(), raceIdentifier.getRaceColumnName(), raceIdentifier.getFleetName(), mark.getIdAsString(), newFix, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editMarkFix(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, MarkDTO mark, GPSFixDTO oldFix,
|
||||
Position newPosition, AsyncCallback<Void> callback) {
|
||||
sailingService.editMarkFix(raceIdentifier.getLeaderboardName(), raceIdentifier.getRaceColumnName(), raceIdentifier.getFleetName(), mark.getIdAsString(), oldFix, newPosition, callback);
|
||||
}
|
||||
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
package com.sap.sailing.gwt.ui.client.shared.charts;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gwt.cell.client.ButtonCell;
|
||||
import com.google.gwt.cell.client.FieldUpdater;
|
||||
import com.google.gwt.cell.client.TextCell;
|
||||
import com.google.gwt.core.client.Callback;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.user.cellview.client.Header;
|
||||
import com.google.gwt.user.cellview.client.TextHeader;
|
||||
import com.google.gwt.user.client.ui.SimplePanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.view.client.ListDataProvider;
|
||||
import com.google.gwt.view.client.SingleSelectionModel;
|
||||
import com.sap.sailing.domain.common.InvertibleComparator;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.SortingOrder;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.EditMarkPositionPanel.NotificationType;
|
||||
import com.sap.sailing.gwt.ui.client.shared.controls.FlushableSortedCellTableWithStylableHeaders;
|
||||
import com.sap.sailing.gwt.ui.client.shared.controls.SortableColumn;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardTableResources;
|
||||
import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
import com.sap.sse.common.settings.AbstractSettings;
|
||||
import com.sap.sse.gwt.client.celltable.EntityIdentityComparator;
|
||||
import com.sap.sse.gwt.client.celltable.RefreshableSingleSelectionModel;
|
||||
import com.sap.sse.gwt.client.shared.components.Component;
|
||||
import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent;
|
||||
|
||||
public class MarksPanel extends SimplePanel implements Component<AbstractSettings> {
|
||||
private static final LeaderboardTableResources tableResources = GWT.create(LeaderboardTableResources.class);
|
||||
|
||||
private final ListDataProvider<MarkDTO> markDataProvider;
|
||||
private final FlushableSortedCellTableWithStylableHeaders<MarkDTO> markTable;
|
||||
|
||||
public MarksPanel(final EditMarkPositionPanel parent, final ListDataProvider<MarkDTO> markDataProvider, final StringMessages stringMessages) {
|
||||
this.markDataProvider = markDataProvider;
|
||||
setTitle(stringMessages.marks());
|
||||
markTable = new FlushableSortedCellTableWithStylableHeaders<MarkDTO>(10000, tableResources);
|
||||
SortableColumn<MarkDTO, String> markNameColumn = new SortableColumn<MarkDTO, String>(new TextCell(), SortingOrder.NONE) {
|
||||
@Override
|
||||
public String getValue(MarkDTO object) {
|
||||
return object.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvertibleComparator<MarkDTO> getComparator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Header<?> getHeader() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
markTable.addColumn(markNameColumn, new TextHeader(stringMessages.marks()));
|
||||
SortableColumn<MarkDTO, String> addFixColumn = new SortableColumn<MarkDTO, String>(new ButtonCell(), SortingOrder.NONE) {
|
||||
@Override
|
||||
public String getValue(MarkDTO object) {
|
||||
return stringMessages.addNewFix();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvertibleComparator<MarkDTO> getComparator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Header<?> getHeader() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
addFixColumn.setFieldUpdater(new FieldUpdater<MarkDTO, String>() {
|
||||
@Override
|
||||
public void update(int index, final MarkDTO mark, String value) {
|
||||
final Date timePoint = parent.timer.getTime();
|
||||
if (parent.hasFixAtTimePoint(mark, timePoint)) {
|
||||
parent.showNotification(stringMessages.pleaseSelectOtherTimepoint(), NotificationType.ERROR);
|
||||
} else {
|
||||
parent.createFixPositionChooserToAddFixToMark(mark, new Callback<Position, Exception>() {
|
||||
@Override
|
||||
public void onFailure(Exception reason) {
|
||||
parent.resetCurrentFixPositionChooser();
|
||||
}
|
||||
@Override
|
||||
public void onSuccess(Position result) {
|
||||
parent.addMarkFix(mark, timePoint, result);
|
||||
parent.resetCurrentFixPositionChooser();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
markTable.addColumn(addFixColumn, new TextHeader(""));
|
||||
SingleSelectionModel<MarkDTO> selectionModel = new RefreshableSingleSelectionModel<MarkDTO>(new EntityIdentityComparator<MarkDTO>() {
|
||||
@Override
|
||||
public boolean representSameEntity(MarkDTO dto1, MarkDTO dto2) {
|
||||
return dto1.getIdAsString().equals(dto2.getIdAsString());
|
||||
}
|
||||
@Override
|
||||
public int hashCode(MarkDTO t) {
|
||||
return t.getIdAsString().hashCode();
|
||||
}
|
||||
},this.markDataProvider);
|
||||
markTable.setSelectionModel(selectionModel);
|
||||
markTable.getSelectionModel().addSelectionChangeHandler(parent);
|
||||
markDataProvider.addDataDisplay(markTable);
|
||||
setWidget(markTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean visible) {
|
||||
super.setVisible(visible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocalizedShortName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Widget getEntryWidget() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSettings() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettingsDialogComponent<AbstractSettings> getSettingsDialogComponent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSettings(AbstractSettings newSettings) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDependentCssClassName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public MarkDTO getSelectedMark() {
|
||||
for (MarkDTO mark : markDataProvider.getList()) {
|
||||
if (markTable.getSelectionModel().isSelected(mark)) {
|
||||
return mark;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void deselectMark() {//FIXME
|
||||
markTable.getSelectionModel().setSelected(getSelectedMark(), false);
|
||||
}
|
||||
|
||||
public void select(MarkDTO mark) {
|
||||
markTable.getSelectionModel().setSelected(mark, true);
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package com.sap.sailing.gwt.ui.client.shared.charts;
|
||||
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.dto.FleetDTO;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
|
||||
|
||||
/**
|
||||
* Given a {@link LeaderboardDTO}, maps a {@link RegattaAndRaceIdentifier} to the triple of
|
||||
* leaderboard, race column and fleet which is useful to identify a race log and/or regatta
|
||||
* log.<p>
|
||||
*
|
||||
* The {@link LeaderboardDTO} can be {@link #setLeaderboard updated} at any time which may affect the result of resolving
|
||||
* the race identifier. A typical case would be changing a race's tracking status or attaching or
|
||||
* detaching the tracked race from the race column.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public class RaceIdentifierToLeaderboardRaceColumnAndFleetMapper {
|
||||
private LeaderboardDTO leaderboard;
|
||||
|
||||
public static class LeaderboardNameRaceColumnNameAndFleetName {
|
||||
private final String leaderboardName;
|
||||
private final String raceColumnName;
|
||||
private final String fleetName;
|
||||
public LeaderboardNameRaceColumnNameAndFleetName(String leaderboardName, String raceColumnName, String fleetName) {
|
||||
super();
|
||||
this.leaderboardName = leaderboardName;
|
||||
this.raceColumnName = raceColumnName;
|
||||
this.fleetName = fleetName;
|
||||
}
|
||||
public String getLeaderboardName() {
|
||||
return leaderboardName;
|
||||
}
|
||||
public String getRaceColumnName() {
|
||||
return raceColumnName;
|
||||
}
|
||||
public String getFleetName() {
|
||||
return fleetName;
|
||||
}
|
||||
}
|
||||
public void setLeaderboard(LeaderboardDTO leaderboard) {
|
||||
this.leaderboard = leaderboard;
|
||||
}
|
||||
|
||||
public LeaderboardDTO getLeaderboard() {
|
||||
return leaderboard;
|
||||
}
|
||||
|
||||
public RaceColumnDTO getColumn(RegattaAndRaceIdentifier raceIdentifier) {
|
||||
RaceColumnDTO result = null;
|
||||
if (leaderboard != null) {
|
||||
for (RaceColumnDTO columnDTO : leaderboard.getRaceList()) {
|
||||
if (columnDTO.containsRace(raceIdentifier)) {
|
||||
result = columnDTO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public LeaderboardNameRaceColumnNameAndFleetName getLeaderboardNameAndRaceColumnNameAndFleetName(RegattaAndRaceIdentifier raceIdentifier) {
|
||||
final LeaderboardNameRaceColumnNameAndFleetName result;
|
||||
final RaceColumnDTO raceColumn = getColumn(raceIdentifier);
|
||||
if (raceColumn == null) {
|
||||
result = null;
|
||||
} else {
|
||||
final FleetDTO fleet = raceColumn.getFleet(raceIdentifier);
|
||||
if (fleet == null) {
|
||||
result = null;
|
||||
} else {
|
||||
result = new LeaderboardNameRaceColumnNameAndFleetName(leaderboard.name, raceColumn.getName(), fleet.getName());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+1
-8
@@ -93,7 +93,7 @@ public class WindChart extends AbstractRaceChart implements Component<WindChartS
|
||||
public WindChart(WindChartLifecycle windChartLifecycle, SailingServiceAsync sailingService, RegattaAndRaceIdentifier selectedRaceIdentifier, Timer timer,
|
||||
TimeRangeWithZoomProvider timeRangeWithZoomProvider, WindChartSettings settings, final StringMessages stringMessages,
|
||||
AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, boolean compactChart) {
|
||||
super(sailingService, timer, timeRangeWithZoomProvider, stringMessages, asyncActionsExecutor, errorReporter);
|
||||
super(sailingService, selectedRaceIdentifier, timer, timeRangeWithZoomProvider, stringMessages, asyncActionsExecutor, errorReporter);
|
||||
this.windChartLifecycle = windChartLifecycle;
|
||||
this.settings = settings;
|
||||
windSourceDirectionSeries = new HashMap<WindSource, Series>();
|
||||
@@ -190,7 +190,6 @@ public class WindChart extends AbstractRaceChart implements Component<WindChartS
|
||||
.getXAxis().setAxisTitle(null);
|
||||
}
|
||||
setSize("100%", "100%");
|
||||
this.selectedRaceIdentifier = selectedRaceIdentifier;
|
||||
if (selectedRaceIdentifier != null) {
|
||||
clearCacheAndReload();
|
||||
if (isVisible()) {
|
||||
@@ -543,12 +542,6 @@ public class WindChart extends AbstractRaceChart implements Component<WindChartS
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTimePlotLine(Date date) {
|
||||
chart.getXAxis().removePlotLine(timePlotLine);
|
||||
timePlotLine.setValue(date.getTime());
|
||||
chart.getXAxis().addPlotLines(timePlotLine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResize() {
|
||||
chart.setSizeToMatchContainer();
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@ import com.sap.sailing.domain.common.LegType;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.dto.BoatClassDTO;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTOWithSpeedWindTackAndLegType;
|
||||
import com.sap.sailing.gwt.ui.shared.SpeedWithBearingDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.racemap.BoatClassVectorGraphics;
|
||||
import com.sap.sailing.gwt.ui.shared.racemap.CanvasOverlayV3;
|
||||
@@ -32,7 +32,7 @@ public class BoatOverlay extends CanvasOverlayV3 {
|
||||
/**
|
||||
* The current GPS fix used to draw the boat.
|
||||
*/
|
||||
private GPSFixDTO boatFix;
|
||||
private GPSFixDTOWithSpeedWindTackAndLegType boatFix;
|
||||
|
||||
/**
|
||||
* The rotation angle of the original boat image in degrees
|
||||
@@ -142,7 +142,7 @@ public class BoatOverlay extends CanvasOverlayV3 {
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoatFix(GPSFixDTO boatFix, long timeForPositionTransitionMillis) {
|
||||
public void setBoatFix(GPSFixDTOWithSpeedWindTackAndLegType boatFix, long timeForPositionTransitionMillis) {
|
||||
updateTransition(timeForPositionTransitionMillis);
|
||||
this.boatFix = boatFix;
|
||||
}
|
||||
|
||||
+6
-1
@@ -23,7 +23,7 @@ public class CourseMarkOverlay extends CanvasOverlayV3 {
|
||||
/**
|
||||
* The course mark to draw
|
||||
*/
|
||||
private MarkDTO mark;
|
||||
private final MarkDTO mark;
|
||||
|
||||
private Position position;
|
||||
|
||||
@@ -171,6 +171,11 @@ public class CourseMarkOverlay extends CanvasOverlayV3 {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public void setMarkPosition(LatLng positionLatLng) {
|
||||
position = coordinateSystem.getPosition(positionLatLng);
|
||||
draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* The real-world position where the mark displayed by this overlay is located
|
||||
*/
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
package com.sap.sailing.gwt.ui.client.shared.racemap;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.google.gwt.canvas.dom.client.Context2d;
|
||||
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.sap.sailing.domain.common.FixType;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.racemap.CanvasOverlayV3;
|
||||
import com.sap.sailing.gwt.ui.shared.racemap.FixVectorGraphics;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class FixOverlay extends CanvasOverlayV3 {
|
||||
private GPSFixDTO fix;
|
||||
private final FixVectorGraphics fixVectorGraphics;
|
||||
private final HashMap<Integer, Util.Pair<Double,Size>> fixScaleAndSizePerZoomCache;
|
||||
|
||||
private Double lastWidth;
|
||||
private Double lastHeight;
|
||||
private Double lastScaleFactor;
|
||||
|
||||
public FixOverlay(MapWidget map, int zIndex, GPSFixDTO fixDTO, FixType type, String color, CoordinateSystem coordinateSystem, String tooltip) {
|
||||
super(map, zIndex, coordinateSystem);
|
||||
fix = fixDTO;
|
||||
fixVectorGraphics = new FixVectorGraphics(type, color);
|
||||
fixScaleAndSizePerZoomCache = new HashMap<Integer, Util.Pair<Double,Size>>();
|
||||
setCanvasSize(50, 50);
|
||||
getCanvas().setTitle(tooltip);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void draw() {
|
||||
if (mapProjection != null && fix != null) {
|
||||
int zoom = map.getZoom();
|
||||
Util.Pair<Double, Size> fixScaleAndSize = fixScaleAndSizePerZoomCache.get(zoom);
|
||||
if (fixScaleAndSize == null) {
|
||||
fixScaleAndSize = getFixScaleAndSize();
|
||||
fixScaleAndSizePerZoomCache.put(zoom, fixScaleAndSize);
|
||||
}
|
||||
double fixSizeScaleFactor = fixScaleAndSize.getA();
|
||||
// calculate canvas size
|
||||
double canvasWidth = fixScaleAndSize.getB().getWidth();
|
||||
double canvasHeight = fixScaleAndSize.getB().getHeight();
|
||||
if (needToDraw(canvasWidth, canvasHeight, fixSizeScaleFactor)) {
|
||||
setCanvasSize((int) canvasWidth, (int) canvasHeight);
|
||||
Context2d context2d = getCanvas().getContext2d();
|
||||
// draw the fix
|
||||
fixVectorGraphics.drawMarkToCanvas(context2d, canvasWidth, canvasHeight, fixSizeScaleFactor);
|
||||
lastScaleFactor = fixSizeScaleFactor;
|
||||
lastWidth = canvasWidth;
|
||||
lastHeight = canvasHeight;
|
||||
}
|
||||
setLatLngPosition(coordinateSystem.toLatLng(fix.position));
|
||||
Point fixPositionInPx = mapProjection.fromLatLngToDivPixel(coordinateSystem.toLatLng(fix.position));
|
||||
setCanvasPosition(fixPositionInPx.getX() - canvasWidth / 2.0, fixPositionInPx.getY() - canvasHeight / 2.0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the drawing parameters to the <code>last...</code>. If anything has
|
||||
* changed, the result is <code>true</code>.
|
||||
*/
|
||||
private boolean needToDraw(double width, double height, double scaleFactor) {
|
||||
return lastScaleFactor == null || lastScaleFactor != scaleFactor ||
|
||||
lastWidth == null || lastWidth != width ||
|
||||
lastHeight == null || lastHeight != height;
|
||||
}
|
||||
|
||||
public Util.Pair<Double, Size> getFixScaleAndSize() {
|
||||
double minFixHeight = 20;
|
||||
|
||||
// the original fix vector graphics is too small (2.1m x 1.5m) for higher zoom levels
|
||||
// therefore we scale the fixes with factor 2 by default
|
||||
double buoyScaleFactor = 2.0;
|
||||
|
||||
Size fixSizeInPixel = calculateBoundingBox(mapProjection, fix.position,
|
||||
fixVectorGraphics.getFixWidthInMeters() * buoyScaleFactor, fixVectorGraphics.getFixHeightInMeters() * buoyScaleFactor);
|
||||
|
||||
double fixHeightInPixel = fixSizeInPixel.getHeight();
|
||||
if(fixHeightInPixel < minFixHeight)
|
||||
fixHeightInPixel = minFixHeight;
|
||||
|
||||
// The coordinates of the canvas drawing methods are based on the 'centimeter' unit (1px = 1cm).
|
||||
// To calculate the display real fix size the scale factor from canvas units to the real
|
||||
double fixSizeScaleFactor = fixHeightInPixel / (fixVectorGraphics.getFixHeightInMeters() * 100);
|
||||
|
||||
return new Util.Pair<Double, Size>(fixSizeScaleFactor, Size.newInstance(fixHeightInPixel * 2.0, fixHeightInPixel * 2.0));
|
||||
}
|
||||
|
||||
public GPSFixDTO getGPSFixDTO() {
|
||||
return fix;
|
||||
}
|
||||
|
||||
public void setGPSFixDTO(GPSFixDTO fix) {
|
||||
this.fix = fix;
|
||||
draw();
|
||||
}
|
||||
|
||||
public String getColor() {
|
||||
return fixVectorGraphics.getColor();
|
||||
}
|
||||
|
||||
public FixType getType() {
|
||||
return fixVectorGraphics.getType();
|
||||
}
|
||||
|
||||
public CoordinateSystem getCoordinateSystem() {
|
||||
return coordinateSystem;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+38
-38
@@ -18,15 +18,15 @@ import com.google.gwt.user.client.Timer;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTOWithSpeedWindTackAndLegType;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Triple;
|
||||
|
||||
/**
|
||||
* Manages the cache of {@link GPSFixDTO}s for the competitors and the polylines encoding the tails that visualize the
|
||||
* Manages the cache of {@link GPSFixDTOWithSpeedWindTackAndLegType}s for the competitors and the polylines encoding the tails that visualize the
|
||||
* course the boats took. The tails are based on the GPS fix data. This class offers methods to update the fixes and
|
||||
* the tails, making sure that the data is always managed consistently. In particular, it keeps an eye on
|
||||
* {@link GPSFixDTO#extrapolated extrapolated fixes}. Those are just a guess where a boat may have been and will need
|
||||
* {@link GPSFixDTOWithSpeedWindTackAndLegType#extrapolated extrapolated fixes}. Those are just a guess where a boat may have been and will need
|
||||
* to be removed once actual data for that time is available.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
@@ -36,16 +36,16 @@ public class FixesAndTails {
|
||||
/**
|
||||
* Fixes of each competitors tail. If a list is contained for a competitor, the list contains a timely "contiguous"
|
||||
* list of fixes for the competitor. This means the server has no more data for the time interval covered, unless
|
||||
* the last fix was {@link GPSFixDTO#extrapolated obtained by extrapolation}.
|
||||
* the last fix was {@link GPSFixDTOWithSpeedWindTackAndLegType#extrapolated obtained by extrapolation}.
|
||||
* <p>
|
||||
*
|
||||
* If the fixes for a competitor contain an {@link GPSFixDTO#extrapolated extrapolated} fix, that fix is always
|
||||
* If the fixes for a competitor contain an {@link GPSFixDTOWithSpeedWindTackAndLegType#extrapolated extrapolated} fix, that fix is always
|
||||
* guaranteed to be the last element of the list when outside the execution of a method on this class. This in
|
||||
* particular means that when more fixes are added, and there is now one fix later than the extrapolated fix, the
|
||||
* extrapolated fix will be removed, re-establishing the invariant of an extrapolated fix always being the last
|
||||
* in the list.
|
||||
*/
|
||||
private final Map<CompetitorDTO, List<GPSFixDTO>> fixes;
|
||||
private final Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> fixes;
|
||||
|
||||
/**
|
||||
* Tails of competitors currently displayed as overlays on the map. A tail may have an {@link MVCArray#getLength()
|
||||
@@ -72,19 +72,19 @@ public class FixesAndTails {
|
||||
|
||||
public FixesAndTails(CoordinateSystem coordinateSystem) {
|
||||
this.coordinateSystem = coordinateSystem;
|
||||
fixes = new HashMap<CompetitorDTO, List<GPSFixDTO>>();
|
||||
fixes = new HashMap<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>>();
|
||||
tails = new HashMap<CompetitorDTO, Polyline>();
|
||||
firstShownFix = new HashMap<CompetitorDTO, Integer>();
|
||||
lastShownFix = new HashMap<CompetitorDTO, Integer>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of fixes cached for the competitor; if a fix is {@link GPSFixDTO#extrapolated extrapolated}, it
|
||||
* @return the list of fixes cached for the competitor; if a fix is {@link GPSFixDTOWithSpeedWindTackAndLegType#extrapolated extrapolated}, it
|
||||
* must be the last fix in the list. <code>null</code> may be returned in no fixes are cached for
|
||||
* <code>competitor</code>. The list returned is unmodifiable for the caller.
|
||||
*/
|
||||
public List<GPSFixDTO> getFixes(CompetitorDTO competitor) {
|
||||
final List<GPSFixDTO> competitorFixes = fixes.get(competitor);
|
||||
public List<GPSFixDTOWithSpeedWindTackAndLegType> getFixes(CompetitorDTO competitor) {
|
||||
final List<GPSFixDTOWithSpeedWindTackAndLegType> competitorFixes = fixes.get(competitor);
|
||||
return competitorFixes == null ? null : Collections.unmodifiableList(competitorFixes);
|
||||
}
|
||||
|
||||
@@ -108,20 +108,20 @@ public class FixesAndTails {
|
||||
}
|
||||
|
||||
/**
|
||||
* When for a competitor the last fix obtained from the server is {@link GPSFixDTO#extrapolated extrapolated}, the quality
|
||||
* When for a competitor the last fix obtained from the server is {@link GPSFixDTOWithSpeedWindTackAndLegType#extrapolated extrapolated}, the quality
|
||||
* of this fix depends on the time difference between the extrapolated fix's time point and the last non-extrapolated
|
||||
* fix's time point. This time difference in milliseconds is returned by this method, or <code>0</code> in case
|
||||
* the last fix for <code>competitor</code> is not extrapolated.
|
||||
*/
|
||||
protected long getMillisecondsBetweenExtrapolatedAndLastNonExtrapolatedFix(CompetitorDTO competitor) {
|
||||
List<GPSFixDTO> competitorFixes = getFixes(competitor);
|
||||
List<GPSFixDTOWithSpeedWindTackAndLegType> competitorFixes = getFixes(competitor);
|
||||
final long result;
|
||||
if (competitorFixes == null || competitorFixes.size() < 2 || !competitorFixes.get(competitorFixes.size()-1).extrapolated) {
|
||||
result = 0;
|
||||
} else {
|
||||
// last fix is extrapolated and another fix present; check time difference
|
||||
GPSFixDTO extrapolatedFix = competitorFixes.get(competitorFixes.size()-1);
|
||||
GPSFixDTO fixBeforeExtrapolated = competitorFixes.get(competitorFixes.size()-2);
|
||||
GPSFixDTOWithSpeedWindTackAndLegType extrapolatedFix = competitorFixes.get(competitorFixes.size()-1);
|
||||
GPSFixDTOWithSpeedWindTackAndLegType fixBeforeExtrapolated = competitorFixes.get(competitorFixes.size()-2);
|
||||
result = extrapolatedFix.timepoint.getTime() - fixBeforeExtrapolated.timepoint.getTime();
|
||||
}
|
||||
return result;
|
||||
@@ -137,13 +137,13 @@ public class FixesAndTails {
|
||||
*/
|
||||
protected Polyline createTailAndUpdateIndices(final CompetitorDTO competitorDTO, Date from, Date to, TailFactory tailFactory) {
|
||||
List<LatLng> points = new ArrayList<LatLng>();
|
||||
List<GPSFixDTO> fixesForCompetitor = getFixes(competitorDTO);
|
||||
List<GPSFixDTOWithSpeedWindTackAndLegType> fixesForCompetitor = getFixes(competitorDTO);
|
||||
int indexOfFirst = -1;
|
||||
int indexOfLast = -1;
|
||||
int i = 0;
|
||||
// TODO consider binary search to find beginning of interesting segment faster
|
||||
for (Iterator<GPSFixDTO> fixIter = fixesForCompetitor.iterator(); fixIter.hasNext() && indexOfLast == -1;) {
|
||||
GPSFixDTO fix = fixIter.next();
|
||||
for (Iterator<GPSFixDTOWithSpeedWindTackAndLegType> fixIter = fixesForCompetitor.iterator(); fixIter.hasNext() && indexOfLast == -1;) {
|
||||
GPSFixDTOWithSpeedWindTackAndLegType fix = fixIter.next();
|
||||
if (!fix.timepoint.before(to)) {
|
||||
indexOfLast = i-1;
|
||||
} else {
|
||||
@@ -187,7 +187,7 @@ public class FixesAndTails {
|
||||
* also {@link #updateTail}).
|
||||
*
|
||||
* @param fixesForCompetitors
|
||||
* For each list the invariant must hold that an {@link GPSFixDTO#extrapolated extrapolated} fix must be
|
||||
* For each list the invariant must hold that an {@link GPSFixDTOWithSpeedWindTackAndLegType#extrapolated extrapolated} fix must be
|
||||
* the last one in the list
|
||||
* @param overlapsWithKnownFixes
|
||||
* if for a competitor whose fixes are provided in <code>fixesForCompetitors</code> this holds
|
||||
@@ -211,15 +211,15 @@ public class FixesAndTails {
|
||||
* we would be producing severe flicker. By delaying the command to the point when the update is applied we
|
||||
* should avoid such flicker.
|
||||
*/
|
||||
protected Map<CompetitorDTO, Runnable> updateFixes(Map<CompetitorDTO, List<GPSFixDTO>> fixesForCompetitors,
|
||||
protected Map<CompetitorDTO, Runnable> updateFixes(Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> fixesForCompetitors,
|
||||
Map<CompetitorDTO, Boolean> overlapsWithKnownFixes, TailFactory tailFactory, long timeForPositionTransitionMillis) {
|
||||
final Map<CompetitorDTO, Runnable> result = new HashMap<>();
|
||||
final Runnable noOp = new Runnable() { @Override public void run() {} };
|
||||
for (final Map.Entry<CompetitorDTO, List<GPSFixDTO>> e : fixesForCompetitors.entrySet()) {
|
||||
for (final Map.Entry<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> e : fixesForCompetitors.entrySet()) {
|
||||
if (e.getValue() != null && !e.getValue().isEmpty()) {
|
||||
List<GPSFixDTO> fixesForCompetitor = fixes.get(e.getKey());
|
||||
List<GPSFixDTOWithSpeedWindTackAndLegType> fixesForCompetitor = fixes.get(e.getKey());
|
||||
if (fixesForCompetitor == null) {
|
||||
fixesForCompetitor = new ArrayList<GPSFixDTO>();
|
||||
fixesForCompetitor = new ArrayList<GPSFixDTOWithSpeedWindTackAndLegType>();
|
||||
fixes.put(e.getKey(), fixesForCompetitor);
|
||||
}
|
||||
if (!overlapsWithKnownFixes.get(e.getKey())) {
|
||||
@@ -255,26 +255,26 @@ public class FixesAndTails {
|
||||
* competitor's tail, the tail is adjusted by inserting the corresponding fix.
|
||||
* <p>
|
||||
*
|
||||
* If the last fix so far was an {@link GPSFixDTO#extrapolated extrapolated} fix, and the merge leads to a different
|
||||
* If the last fix so far was an {@link GPSFixDTOWithSpeedWindTackAndLegType#extrapolated extrapolated} fix, and the merge leads to a different
|
||||
* fix being the last one shown, the previously last fix that was extrapolated will be removed. This way, at most
|
||||
* one extrapolated fix is shown, avoiding jitter on the map as actual fixes are received that obsolete the
|
||||
* extrapolated ones.<p>
|
||||
*
|
||||
* Precondition: {@link #hasFixesFor(CompetitorDTO) hasFixesFor(competitorDTO)}<code>==true</code>
|
||||
* @param mergeThis
|
||||
* If this list contains an {@link GPSFixDTO#extrapolated extrapolated} fix, that fix must be the last in
|
||||
* If this list contains an {@link GPSFixDTOWithSpeedWindTackAndLegType#extrapolated extrapolated} fix, that fix must be the last in
|
||||
* the list
|
||||
*/
|
||||
private void mergeFixes(CompetitorDTO competitorDTO, List<GPSFixDTO> mergeThis, final long timeForPositionTransitionMillis) {
|
||||
List<GPSFixDTO> intoThis = fixes.get(competitorDTO);
|
||||
private void mergeFixes(CompetitorDTO competitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType> mergeThis, final long timeForPositionTransitionMillis) {
|
||||
List<GPSFixDTOWithSpeedWindTackAndLegType> intoThis = fixes.get(competitorDTO);
|
||||
int indexOfFirstShownFix = firstShownFix.get(competitorDTO) == null ? -1 : firstShownFix.get(competitorDTO);
|
||||
int indexOfLastShownFix = lastShownFix.get(competitorDTO) == null ? -1 : lastShownFix.get(competitorDTO);
|
||||
final Polyline tail = getTail(competitorDTO);
|
||||
int intoThisIndex = 0;
|
||||
for (GPSFixDTO mergeThisFix : mergeThis) {
|
||||
intoThisIndex = Collections.binarySearch(intoThis, mergeThisFix, new Comparator<GPSFixDTO>() {
|
||||
for (GPSFixDTOWithSpeedWindTackAndLegType mergeThisFix : mergeThis) {
|
||||
intoThisIndex = Collections.binarySearch(intoThis, mergeThisFix, new Comparator<GPSFixDTOWithSpeedWindTackAndLegType>() {
|
||||
@Override
|
||||
public int compare(GPSFixDTO o1, GPSFixDTO o2) {
|
||||
public int compare(GPSFixDTOWithSpeedWindTackAndLegType o1, GPSFixDTOWithSpeedWindTackAndLegType o2) {
|
||||
return o1.timepoint.compareTo(o2.timepoint);
|
||||
}
|
||||
});
|
||||
@@ -396,7 +396,7 @@ public class FixesAndTails {
|
||||
tailPreparer.run();
|
||||
}
|
||||
int vertexCount = tail.getPath().getLength();
|
||||
final List<GPSFixDTO> fixesForCompetitor = getFixes(competitorDTO);
|
||||
final List<GPSFixDTOWithSpeedWindTackAndLegType> fixesForCompetitor = getFixes(competitorDTO);
|
||||
int indexOfFirstShownFix = firstShownFix.get(competitorDTO) == null ? -1 : firstShownFix.get(competitorDTO);
|
||||
// remove fixes before what is now to be the beginning of the polyline:
|
||||
while (indexOfFirstShownFix != -1 && vertexCount > 0
|
||||
@@ -411,7 +411,7 @@ public class FixesAndTails {
|
||||
while (indexOfFirstShownFix > 0
|
||||
&& !fixesForCompetitor.get(indexOfFirstShownFix - 1).timepoint.before(from)) {
|
||||
indexOfFirstShownFix--;
|
||||
GPSFixDTO fix = fixesForCompetitor.get(indexOfFirstShownFix);
|
||||
GPSFixDTOWithSpeedWindTackAndLegType fix = fixesForCompetitor.get(indexOfFirstShownFix);
|
||||
tail.getPath().insertAt(0, coordinateSystem.toLatLng(fix.position));
|
||||
vertexCount++;
|
||||
}
|
||||
@@ -431,7 +431,7 @@ public class FixesAndTails {
|
||||
while (indexOfLastShownFix < fixesForCompetitor.size() - 1
|
||||
&& !fixesForCompetitor.get(indexOfLastShownFix + 1).timepoint.after(to)) {
|
||||
indexOfLastShownFix++;
|
||||
GPSFixDTO fix = fixesForCompetitor.get(indexOfLastShownFix);
|
||||
GPSFixDTOWithSpeedWindTackAndLegType fix = fixesForCompetitor.get(indexOfLastShownFix);
|
||||
tail.getPath().insertAt(vertexCount++, coordinateSystem.toLatLng(fix.position));
|
||||
if (indexOfFirstShownFix < 0) { // empty tail before?
|
||||
indexOfFirstShownFix = indexOfLastShownFix; // set to the first vertex inserted into tail
|
||||
@@ -500,7 +500,7 @@ public class FixesAndTails {
|
||||
Map<CompetitorDTO, Boolean> overlapWithKnownFixes = new HashMap<CompetitorDTO, Boolean>();
|
||||
|
||||
for (CompetitorDTO competitor : competitorsToShow) {
|
||||
List<GPSFixDTO> fixesForCompetitor = getFixes(competitor);
|
||||
List<GPSFixDTOWithSpeedWindTackAndLegType> fixesForCompetitor = getFixes(competitor);
|
||||
Date fromDate;
|
||||
Date toDate;
|
||||
Date timepointOfLastKnownFix = fixesForCompetitor == null ? null : getTimepointOfLastNonExtrapolated(fixesForCompetitor);
|
||||
@@ -535,8 +535,8 @@ public class FixesAndTails {
|
||||
overlapWithKnownFixes);
|
||||
}
|
||||
|
||||
private Date getTimepointOfFirstNonExtrapolated(List<GPSFixDTO> fixesForCompetitor) {
|
||||
for (GPSFixDTO fix : fixesForCompetitor) {
|
||||
private Date getTimepointOfFirstNonExtrapolated(List<GPSFixDTOWithSpeedWindTackAndLegType> fixesForCompetitor) {
|
||||
for (GPSFixDTOWithSpeedWindTackAndLegType fix : fixesForCompetitor) {
|
||||
if (!fix.extrapolated) {
|
||||
return fix.timepoint;
|
||||
}
|
||||
@@ -544,11 +544,11 @@ public class FixesAndTails {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Date getTimepointOfLastNonExtrapolated(List<GPSFixDTO> fixesForCompetitor) {
|
||||
private Date getTimepointOfLastNonExtrapolated(List<GPSFixDTOWithSpeedWindTackAndLegType> fixesForCompetitor) {
|
||||
if (!fixesForCompetitor.isEmpty()) {
|
||||
for (ListIterator<GPSFixDTO> fixIter = fixesForCompetitor.listIterator(fixesForCompetitor.size() - 1); fixIter
|
||||
for (ListIterator<GPSFixDTOWithSpeedWindTackAndLegType> fixIter = fixesForCompetitor.listIterator(fixesForCompetitor.size() - 1); fixIter
|
||||
.hasPrevious();) {
|
||||
GPSFixDTO fix = fixIter.previous();
|
||||
GPSFixDTOWithSpeedWindTackAndLegType fix = fixIter.previous();
|
||||
if (!fix.extrapolated) {
|
||||
return fix.timepoint;
|
||||
}
|
||||
|
||||
+206
-116
@@ -22,6 +22,7 @@ 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.event.shared.HandlerRegistration;
|
||||
import com.google.gwt.i18n.client.NumberFormat;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
|
||||
import com.google.gwt.maps.client.LoadApi;
|
||||
@@ -104,7 +105,7 @@ import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil;
|
||||
import com.sap.sailing.gwt.ui.shared.CompactBoatPositionsDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.ControlPointDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.CoursePositionsDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTOWithSpeedWindTackAndLegType;
|
||||
import com.sap.sailing.gwt.ui.shared.LegInfoDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
@@ -248,7 +249,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
private WindInfoForRaceDTO lastCombinedWindTrackInfoDTO;
|
||||
|
||||
/**
|
||||
* Manages the cached set of {@link GPSFixDTO}s for the boat positions as well as their graphical counterpart in the
|
||||
* Manages the cached set of {@link GPSFixDTOWithSpeedWindTackAndLegType}s for the boat positions as well as their graphical counterpart in the
|
||||
* form of {@link Polyline}s.
|
||||
*/
|
||||
private final FixesAndTails fixesAndTails;
|
||||
@@ -274,6 +275,8 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
* Map overlays with html5 canvas used to display course marks including buoy zones
|
||||
*/
|
||||
private final Map<String, CourseMarkOverlay> courseMarkOverlays;
|
||||
|
||||
private final Map<String, HandlerRegistration> courseMarkClickHandlers;
|
||||
|
||||
private final Map<String, MarkDTO> markDTOs;
|
||||
|
||||
@@ -291,7 +294,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
|
||||
private Map<CompetitorDTO, List<ManeuverDTO>> lastManeuverResult;
|
||||
|
||||
private Map<CompetitorDTO, List<GPSFixDTO>> lastDouglasPeuckerResult;
|
||||
private Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> lastDouglasPeuckerResult;
|
||||
|
||||
private final CompetitorSelectionProvider competitorSelection;
|
||||
|
||||
@@ -427,6 +430,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
competitorInfoOverlays = new HashMap<CompetitorDTO, CompetitorInfoOverlay>();
|
||||
windSensorOverlays = new HashMap<WindSource, WindSensorOverlay>();
|
||||
courseMarkOverlays = new HashMap<String, CourseMarkOverlay>();
|
||||
courseMarkClickHandlers = new HashMap<String, HandlerRegistration>();
|
||||
this.competitorSelection = competitorSelection;
|
||||
this.raceCompetitorSet = new RaceCompetitorSet(competitorSelection);
|
||||
competitorSelection.addCompetitorSelectionChangeListener(this);
|
||||
@@ -944,7 +948,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), /* clearCanvas */ false, raceMapDataDTO.simulationResultVersion);
|
||||
}
|
||||
// Do boat specific actions
|
||||
Map<CompetitorDTO, List<GPSFixDTO>> boatData = raceMapDataDTO.boatPositions;
|
||||
Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> boatData = raceMapDataDTO.boatPositions;
|
||||
updateBoatPositions(newTime, transitionTimeInMillis, hasTailOverlapForCompetitor, competitorsToShow, boatData, /* updateTailsOnly */ false);
|
||||
if (douglasMarkers != null) {
|
||||
removeAllMarkDouglasPeuckerpoints();
|
||||
@@ -1016,7 +1020,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
*/
|
||||
private void updateBoatPositions(final Date newTime, final long transitionTimeInMillis,
|
||||
final Map<CompetitorDTO, Boolean> hasTailOverlapForCompetitor,
|
||||
final Iterable<CompetitorDTO> competitorsToShow, Map<CompetitorDTO, List<GPSFixDTO>> boatData, boolean updateTailsOnly) {
|
||||
final Iterable<CompetitorDTO> competitorsToShow, Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> boatData, boolean updateTailsOnly) {
|
||||
final Map<CompetitorDTO, Runnable> tailPreparersPerCompetitor =
|
||||
fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis);
|
||||
showBoatsOnMap(newTime, transitionTimeInMillis, competitorsToShow, updateTailsOnly, tailPreparersPerCompetitor);
|
||||
@@ -1099,12 +1103,13 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
}
|
||||
CourseMarkOverlay courseMarkOverlay = courseMarkOverlays.get(markDTO.getName());
|
||||
if (courseMarkOverlay == null) {
|
||||
courseMarkOverlay = createCourseMarkOverlay(RaceMapOverlaysZIndexes.COURSEMARK_ZINDEX, markDTO);
|
||||
courseMarkOverlay = new CourseMarkOverlay(map, RaceMapOverlaysZIndexes.COURSEMARK_ZINDEX, markDTO, coordinateSystem);
|
||||
courseMarkOverlay.setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE));
|
||||
courseMarkOverlay.setBuoyZoneRadiusInMeter(settings.getBuoyZoneRadiusInMeters());
|
||||
courseMarkOverlay.setSelected(isSelected);
|
||||
courseMarkOverlays.put(markDTO.getName(), courseMarkOverlay);
|
||||
markDTOs.put(markDTO.getName(), markDTO);
|
||||
registerCourseMarkInfoWindowClickHandler(markDTO.getName());
|
||||
courseMarkOverlay.addToMap();
|
||||
} else {
|
||||
courseMarkOverlay.setMarkPosition(markDTO.position, transitionTimeInMillis);
|
||||
@@ -1185,7 +1190,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
competitorInfoOverlays.keySet());
|
||||
for (CompetitorDTO competitorDTO : competitorsToShow) {
|
||||
if (fixesAndTails.hasFixesFor(competitorDTO)) {
|
||||
GPSFixDTO lastBoatFix = getBoatFix(competitorDTO, newTime);
|
||||
GPSFixDTOWithSpeedWindTackAndLegType lastBoatFix = getBoatFix(competitorDTO, newTime);
|
||||
if (lastBoatFix != null) {
|
||||
CompetitorInfoOverlay competitorInfoOverlay = competitorInfoOverlays.get(competitorDTO);
|
||||
if (competitorInfoOverlay == null) {
|
||||
@@ -1332,7 +1337,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
com.sap.sse.common.Util.Pair<Integer, CompetitorDTO> visibleLeaderInfo = getFarthestAheadVisibleCompetitorWithOneBasedLegNumber(competitorsToShow);
|
||||
// the boat fix may be null; may mean that no positions were loaded yet for the leading visible boat;
|
||||
// don't show anything
|
||||
GPSFixDTO lastBoatFix = null;
|
||||
GPSFixDTOWithSpeedWindTackAndLegType lastBoatFix = null;
|
||||
boolean isVisibleLeaderInfoComplete = false;
|
||||
boolean isLegTypeKnown = false;
|
||||
WindTrackInfoDTO windDataForLegMiddle = null;
|
||||
@@ -1448,39 +1453,41 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
|
||||
private void showStartLineToFirstMarkTriangle(final CoursePositionsDTO courseDTO){
|
||||
final List<Position> startMarkPositions = courseDTO.getStartMarkPositions();
|
||||
final Position windwardStartLinePosition = startMarkPositions.get(0);
|
||||
final Position leewardStartLinePosition = startMarkPositions.get(1);
|
||||
final Position firstMarkPosition = courseDTO.waypointPositions.get(1);
|
||||
windwardStartLineMarkToFirstMarkLineText.replace(0, windwardStartLineMarkToFirstMarkLineText.length(),
|
||||
stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal
|
||||
.format(windwardStartLinePosition.getDistance(firstMarkPosition)
|
||||
.getMeters())));
|
||||
leewardStartLineMarkToFirstMarkLineText.replace(0, leewardStartLineMarkToFirstMarkLineText.length(),
|
||||
stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal
|
||||
.format(leewardStartLinePosition.getDistance(firstMarkPosition)
|
||||
.getMeters())));
|
||||
final LineInfoProvider windwardStartLineMarkToFirstMarkLineInfoProvider = new LineInfoProvider() {
|
||||
@Override
|
||||
public String getLineInfo() {
|
||||
return windwardStartLineMarkToFirstMarkLineText.toString();
|
||||
}
|
||||
};
|
||||
final LineInfoProvider leewardStartLineMarkToFirstMarkLineInfoProvider = new LineInfoProvider() {
|
||||
@Override
|
||||
public String getLineInfo() {
|
||||
return leewardStartLineMarkToFirstMarkLineText.toString();
|
||||
}
|
||||
};
|
||||
windwardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(windwardStartLineMarkToFirstMarkLine, /* showLine */
|
||||
(settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE))
|
||||
&& startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1,
|
||||
windwardStartLinePosition, firstMarkPosition, windwardStartLineMarkToFirstMarkLineInfoProvider,
|
||||
"grey");
|
||||
leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* showLine */
|
||||
(settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE))
|
||||
&& startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1,
|
||||
leewardStartLinePosition, firstMarkPosition, leewardStartLineMarkToFirstMarkLineInfoProvider,
|
||||
"grey");
|
||||
if (startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1) {
|
||||
final Position windwardStartLinePosition = startMarkPositions.get(0);
|
||||
final Position leewardStartLinePosition = startMarkPositions.get(1);
|
||||
final Position firstMarkPosition = courseDTO.waypointPositions.get(1);
|
||||
windwardStartLineMarkToFirstMarkLineText.replace(0, windwardStartLineMarkToFirstMarkLineText.length(),
|
||||
stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal
|
||||
.format(windwardStartLinePosition.getDistance(firstMarkPosition)
|
||||
.getMeters())));
|
||||
leewardStartLineMarkToFirstMarkLineText.replace(0, leewardStartLineMarkToFirstMarkLineText.length(),
|
||||
stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal
|
||||
.format(leewardStartLinePosition.getDistance(firstMarkPosition)
|
||||
.getMeters())));
|
||||
final LineInfoProvider windwardStartLineMarkToFirstMarkLineInfoProvider = new LineInfoProvider() {
|
||||
@Override
|
||||
public String getLineInfo() {
|
||||
return windwardStartLineMarkToFirstMarkLineText.toString();
|
||||
}
|
||||
};
|
||||
final LineInfoProvider leewardStartLineMarkToFirstMarkLineInfoProvider = new LineInfoProvider() {
|
||||
@Override
|
||||
public String getLineInfo() {
|
||||
return leewardStartLineMarkToFirstMarkLineText.toString();
|
||||
}
|
||||
};
|
||||
windwardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(windwardStartLineMarkToFirstMarkLine, /* showLine */
|
||||
(settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE))
|
||||
&& startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1,
|
||||
windwardStartLinePosition, firstMarkPosition, windwardStartLineMarkToFirstMarkLineInfoProvider,
|
||||
"grey");
|
||||
leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* showLine */
|
||||
(settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE))
|
||||
&& startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1,
|
||||
leewardStartLinePosition, firstMarkPosition, leewardStartLineMarkToFirstMarkLineInfoProvider,
|
||||
"grey");
|
||||
}
|
||||
}
|
||||
|
||||
private final StringBuilder startLineAdvantageText = new StringBuilder();
|
||||
@@ -1654,47 +1661,49 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
*/
|
||||
private Polyline showOrRemoveOrUpdateLine(Polyline lineToShowOrRemoveOrUpdate, final boolean showLine,
|
||||
final Position position1DTO, final Position position2DTO, final LineInfoProvider lineInfoProvider, String lineColorRGB) {
|
||||
if (showLine) {
|
||||
LatLng courseMiddleLinePoint1 = coordinateSystem.toLatLng(position1DTO);
|
||||
LatLng courseMiddleLinePoint2 = coordinateSystem.toLatLng(position2DTO);
|
||||
final MVCArray<LatLng> pointsAsArray;
|
||||
if (lineToShowOrRemoveOrUpdate == null) {
|
||||
PolylineOptions options = PolylineOptions.newInstance();
|
||||
options.setClickable(true);
|
||||
options.setGeodesic(true);
|
||||
options.setStrokeColor(lineColorRGB);
|
||||
options.setStrokeWeight(1);
|
||||
options.setStrokeOpacity(1.0);
|
||||
pointsAsArray = MVCArray.newInstance();
|
||||
lineToShowOrRemoveOrUpdate = Polyline.newInstance(options);
|
||||
lineToShowOrRemoveOrUpdate.setPath(pointsAsArray);
|
||||
lineToShowOrRemoveOrUpdate.setMap(map);
|
||||
Hoverline lineToShowOrRemoveOrUpdateHoverline = new Hoverline(lineToShowOrRemoveOrUpdate, options, this);
|
||||
lineToShowOrRemoveOrUpdate.addMouseOverHandler(new MouseOverMapHandler() {
|
||||
@Override
|
||||
public void onEvent(MouseOverMapEvent event) {
|
||||
map.setTitle(lineInfoProvider.getLineInfo());
|
||||
}
|
||||
});
|
||||
lineToShowOrRemoveOrUpdateHoverline.addMouseOutMoveHandler(new MouseOutMapHandler() {
|
||||
@Override
|
||||
public void onEvent(MouseOutMapEvent event) {
|
||||
map.setTitle("");
|
||||
}
|
||||
});
|
||||
if (position1DTO != null && position2DTO != null) {
|
||||
if (showLine) {
|
||||
LatLng courseMiddleLinePoint1 = coordinateSystem.toLatLng(position1DTO);
|
||||
LatLng courseMiddleLinePoint2 = coordinateSystem.toLatLng(position2DTO);
|
||||
final MVCArray<LatLng> pointsAsArray;
|
||||
if (lineToShowOrRemoveOrUpdate == null) {
|
||||
PolylineOptions options = PolylineOptions.newInstance();
|
||||
options.setClickable(true);
|
||||
options.setGeodesic(true);
|
||||
options.setStrokeColor(lineColorRGB);
|
||||
options.setStrokeWeight(1);
|
||||
options.setStrokeOpacity(1.0);
|
||||
pointsAsArray = MVCArray.newInstance();
|
||||
lineToShowOrRemoveOrUpdate = Polyline.newInstance(options);
|
||||
lineToShowOrRemoveOrUpdate.setPath(pointsAsArray);
|
||||
lineToShowOrRemoveOrUpdate.setMap(map);
|
||||
Hoverline lineToShowOrRemoveOrUpdateHoverline = new Hoverline(lineToShowOrRemoveOrUpdate, options, this);
|
||||
lineToShowOrRemoveOrUpdate.addMouseOverHandler(new MouseOverMapHandler() {
|
||||
@Override
|
||||
public void onEvent(MouseOverMapEvent event) {
|
||||
map.setTitle(lineInfoProvider.getLineInfo());
|
||||
}
|
||||
});
|
||||
lineToShowOrRemoveOrUpdateHoverline.addMouseOutMoveHandler(new MouseOutMapHandler() {
|
||||
@Override
|
||||
public void onEvent(MouseOutMapEvent event) {
|
||||
map.setTitle("");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
pointsAsArray = lineToShowOrRemoveOrUpdate.getPath();
|
||||
pointsAsArray.removeAt(1);
|
||||
pointsAsArray.removeAt(0);
|
||||
}
|
||||
adjustInfoOverlayForVisibleLine(lineToShowOrRemoveOrUpdate, position1DTO, position2DTO, lineInfoProvider);
|
||||
pointsAsArray.insertAt(0, courseMiddleLinePoint1);
|
||||
pointsAsArray.insertAt(1, courseMiddleLinePoint2);
|
||||
} else {
|
||||
pointsAsArray = lineToShowOrRemoveOrUpdate.getPath();
|
||||
pointsAsArray.removeAt(1);
|
||||
pointsAsArray.removeAt(0);
|
||||
}
|
||||
adjustInfoOverlayForVisibleLine(lineToShowOrRemoveOrUpdate, position1DTO, position2DTO, lineInfoProvider);
|
||||
pointsAsArray.insertAt(0, courseMiddleLinePoint1);
|
||||
pointsAsArray.insertAt(1, courseMiddleLinePoint2);
|
||||
} else {
|
||||
if (lineToShowOrRemoveOrUpdate != null) {
|
||||
lineToShowOrRemoveOrUpdate.setMap(null);
|
||||
adjustInfoOverlayForRemovedLine(lineToShowOrRemoveOrUpdate);
|
||||
lineToShowOrRemoveOrUpdate = null;
|
||||
if (lineToShowOrRemoveOrUpdate != null) {
|
||||
lineToShowOrRemoveOrUpdate.setMap(null);
|
||||
adjustInfoOverlayForRemovedLine(lineToShowOrRemoveOrUpdate);
|
||||
lineToShowOrRemoveOrUpdate = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return lineToShowOrRemoveOrUpdate;
|
||||
@@ -1766,7 +1775,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
return Math.log((1 + sine) / (1 - sine)) / 2;
|
||||
}
|
||||
|
||||
private int getZoomLevel(LatLngBounds bounds) {
|
||||
public int getZoomLevel(LatLngBounds bounds) {
|
||||
int GLOBE_PXSIZE = 256; // a constant in Google's map projection
|
||||
int MAX_ZOOM = 20; // maximum zoom-level that should be automatically selected
|
||||
double LOG2 = Math.log(2.0);
|
||||
@@ -1846,7 +1855,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
*/
|
||||
private boolean updateBoatCanvasForCompetitor(CompetitorDTO competitorDTO, Date date, long timeForPositionTransitionMillis) {
|
||||
boolean usedExistingCanvas = false;
|
||||
GPSFixDTO lastBoatFix = getBoatFix(competitorDTO, date);
|
||||
GPSFixDTOWithSpeedWindTackAndLegType lastBoatFix = getBoatFix(competitorDTO, date);
|
||||
if (lastBoatFix != null) {
|
||||
BoatOverlay boatOverlay = boatOverlays.get(competitorDTO);
|
||||
if (boatOverlay == null) {
|
||||
@@ -1869,17 +1878,42 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
private boolean displayHighlighted(CompetitorDTO competitorDTO) {
|
||||
return !settings.isShowOnlySelectedCompetitors() && competitorSelection.isSelected(competitorDTO);
|
||||
}
|
||||
|
||||
protected CourseMarkOverlay createCourseMarkOverlay(int zIndex, final MarkDTO markDTO) {
|
||||
final CourseMarkOverlay courseMarkOverlay = new CourseMarkOverlay(map, zIndex, markDTO, coordinateSystem);
|
||||
courseMarkOverlay.addClickHandler(new ClickMapHandler() {
|
||||
@Override
|
||||
public void onEvent(ClickMapEvent event) {
|
||||
LatLng latlng = courseMarkOverlay.getMarkLatLngPosition();
|
||||
showMarkInfoWindow(markDTO, latlng);
|
||||
}
|
||||
});
|
||||
return courseMarkOverlay;
|
||||
|
||||
private class CourseMarkInfoWindowClickHandler implements ClickMapHandler {
|
||||
private final MarkDTO markDTO;
|
||||
private final CourseMarkOverlay courseMarkOverlay;
|
||||
|
||||
public CourseMarkInfoWindowClickHandler(MarkDTO markDTO, CourseMarkOverlay courseMarkOverlay) {
|
||||
this.markDTO = markDTO;
|
||||
this.courseMarkOverlay = courseMarkOverlay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(ClickMapEvent event) {
|
||||
LatLng latlng = courseMarkOverlay.getMarkLatLngPosition();
|
||||
showMarkInfoWindow(markDTO, latlng);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerCourseMarkInfoWindowClickHandler(final String markDTOName) {
|
||||
final CourseMarkOverlay courseMarkOverlay = courseMarkOverlays.get(markDTOName);
|
||||
courseMarkClickHandlers.put(markDTOName,
|
||||
courseMarkOverlay.addClickHandler(new CourseMarkInfoWindowClickHandler(markDTOs.get(markDTOName), courseMarkOverlay)));
|
||||
}
|
||||
|
||||
public void registerAllCourseMarkInfoWindowClickHandlers() {
|
||||
for (String markDTOName : markDTOs.keySet()) {
|
||||
registerCourseMarkInfoWindowClickHandler(markDTOName);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterAllCourseMarkInfoWindowClickHandlers() {
|
||||
Iterator<Entry<String, HandlerRegistration>> iterator = courseMarkClickHandlers.entrySet().iterator();
|
||||
while(iterator.hasNext()) {
|
||||
Entry<String, HandlerRegistration> handler = iterator.next();
|
||||
handler.getValue().removeHandler();
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private CompetitorInfoOverlay createCompetitorInfoOverlay(int zIndex, final CompetitorDTO competitorDTO) {
|
||||
@@ -1896,7 +1930,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
if (lastInfoWindow != null) {
|
||||
lastInfoWindow.close();
|
||||
}
|
||||
GPSFixDTO latestFixForCompetitor = getBoatFix(competitorDTO, timer.getTime());
|
||||
GPSFixDTOWithSpeedWindTackAndLegType latestFixForCompetitor = getBoatFix(competitorDTO, timer.getTime());
|
||||
LatLng where = coordinateSystem.toLatLng(latestFixForCompetitor.position);
|
||||
InfoWindowOptions options = InfoWindowOptions.newInstance();
|
||||
InfoWindow infoWindow = InfoWindow.newInstance(options);
|
||||
@@ -1951,7 +1985,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
if(lastInfoWindow != null) {
|
||||
lastInfoWindow.close();
|
||||
}
|
||||
GPSFixDTO latestFixForCompetitor = getBoatFix(competitorDTO, timer.getTime());
|
||||
GPSFixDTOWithSpeedWindTackAndLegType latestFixForCompetitor = getBoatFix(competitorDTO, timer.getTime());
|
||||
// TODO find close fix where the mouse was; see BUG 470
|
||||
InfoWindowOptions options = InfoWindowOptions.newInstance();
|
||||
InfoWindow infoWindow = InfoWindow.newInstance(options);
|
||||
@@ -2045,7 +2079,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
return vPanel;
|
||||
}
|
||||
|
||||
private Widget getInfoWindowContent(CompetitorDTO competitorDTO, GPSFixDTO lastFix) {
|
||||
private Widget getInfoWindowContent(CompetitorDTO competitorDTO, GPSFixDTOWithSpeedWindTackAndLegType lastFix) {
|
||||
final VerticalPanel vPanel = new VerticalPanel();
|
||||
vPanel.setWidth("350px");
|
||||
vPanel.add(createInfoWindowLabelAndValue(stringMessages.competitor(), competitorDTO.getName()));
|
||||
@@ -2080,14 +2114,14 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
Map<CompetitorDTO, Date> to = new HashMap<CompetitorDTO, Date>();
|
||||
to.put(competitorDTO, getBoatFix(competitorDTO, timer.getTime()).timepoint);
|
||||
sailingService.getDouglasPoints(race, from, to, 3,
|
||||
new AsyncCallback<Map<CompetitorDTO, List<GPSFixDTO>>>() {
|
||||
new AsyncCallback<Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError("Error obtaining douglas positions: " + caught.getMessage(), true /*silentMode */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Map<CompetitorDTO, List<GPSFixDTO>> result) {
|
||||
public void onSuccess(Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> result) {
|
||||
lastDouglasPeuckerResult = result;
|
||||
if (douglasMarkers != null) {
|
||||
removeAllMarkDouglasPeuckerpoints();
|
||||
@@ -2169,15 +2203,15 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
}
|
||||
}
|
||||
|
||||
private void showMarkDouglasPeuckerPoints(Map<CompetitorDTO, List<GPSFixDTO>> gpsFixPointMapForCompetitors) {
|
||||
private void showMarkDouglasPeuckerPoints(Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> gpsFixPointMapForCompetitors) {
|
||||
douglasMarkers = new HashSet<Marker>();
|
||||
if (map != null && gpsFixPointMapForCompetitors != null) {
|
||||
Set<CompetitorDTO> keySet = gpsFixPointMapForCompetitors.keySet();
|
||||
Iterator<CompetitorDTO> iter = keySet.iterator();
|
||||
while (iter.hasNext()) {
|
||||
CompetitorDTO competitorDTO = iter.next();
|
||||
List<GPSFixDTO> gpsFix = gpsFixPointMapForCompetitors.get(competitorDTO);
|
||||
for (GPSFixDTO fix : gpsFix) {
|
||||
List<GPSFixDTOWithSpeedWindTackAndLegType> gpsFix = gpsFixPointMapForCompetitors.get(competitorDTO);
|
||||
for (GPSFixDTOWithSpeedWindTackAndLegType fix : gpsFix) {
|
||||
LatLng latLng = coordinateSystem.toLatLng(fix.position);
|
||||
MarkerOptions options = MarkerOptions.newInstance();
|
||||
options.setTitle(fix.timepoint+": "+fix.position+", "+fix.speedWithBearing.toString());
|
||||
@@ -2229,14 +2263,14 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
* @return The GPS fix for the given competitor from {@link #fixes} that is closest to <code>date</code>, or
|
||||
* <code>null</code> if no fix is available
|
||||
*/
|
||||
private GPSFixDTO getBoatFix(CompetitorDTO competitorDTO, Date date) {
|
||||
GPSFixDTO result = null;
|
||||
List<GPSFixDTO> competitorFixes = fixesAndTails.getFixes(competitorDTO);
|
||||
private GPSFixDTOWithSpeedWindTackAndLegType getBoatFix(CompetitorDTO competitorDTO, Date date) {
|
||||
GPSFixDTOWithSpeedWindTackAndLegType result = null;
|
||||
List<GPSFixDTOWithSpeedWindTackAndLegType> competitorFixes = fixesAndTails.getFixes(competitorDTO);
|
||||
if (competitorFixes != null && !competitorFixes.isEmpty()) {
|
||||
int i = Collections.binarySearch(competitorFixes, new GPSFixDTO(date, null, null, (WindDTO) null, null, null, false),
|
||||
new Comparator<GPSFixDTO>() {
|
||||
int i = Collections.binarySearch(competitorFixes, new GPSFixDTOWithSpeedWindTackAndLegType(date, null, null, (WindDTO) null, null, null, false),
|
||||
new Comparator<GPSFixDTOWithSpeedWindTackAndLegType>() {
|
||||
@Override
|
||||
public int compare(GPSFixDTO o1, GPSFixDTO o2) {
|
||||
public int compare(GPSFixDTOWithSpeedWindTackAndLegType o1, GPSFixDTOWithSpeedWindTackAndLegType o2) {
|
||||
return o1.timepoint.compareTo(o2.timepoint);
|
||||
}
|
||||
});
|
||||
@@ -2251,9 +2285,9 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
} else {
|
||||
// competitorFixes must have at least two elements, and i points neither to the end nor the beginning;
|
||||
// get the fix from i and i+1 whose timepoint is closer to date
|
||||
final GPSFixDTO fixBefore = competitorFixes.get(i-1);
|
||||
final GPSFixDTO fixAfter = competitorFixes.get(i);
|
||||
final GPSFixDTO closer;
|
||||
final GPSFixDTOWithSpeedWindTackAndLegType fixBefore = competitorFixes.get(i-1);
|
||||
final GPSFixDTOWithSpeedWindTackAndLegType fixAfter = competitorFixes.get(i);
|
||||
final GPSFixDTOWithSpeedWindTackAndLegType closer;
|
||||
if (date.getTime() - fixBefore.timepoint.getTime() < fixAfter.timepoint.getTime() - date.getTime()) {
|
||||
closer = fixBefore;
|
||||
} else {
|
||||
@@ -2282,12 +2316,12 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
factorForBefore*(fixBefore.speedWithBearing==null?0:fixBefore.speedWithBearing.speedInKnots) +
|
||||
factorForAfter*(fixAfter.speedWithBearing==null?0:fixAfter.speedWithBearing.speedInKnots),
|
||||
betweenBearing);
|
||||
result = new GPSFixDTO(date, betweenPosition, betweenSpeed, closer.degreesBoatToTheWind,
|
||||
result = new GPSFixDTOWithSpeedWindTackAndLegType(date, betweenPosition, betweenSpeed, closer.degreesBoatToTheWind,
|
||||
closer.tack, closer.legType, fixBefore.extrapolated || fixAfter.extrapolated);
|
||||
}
|
||||
} else {
|
||||
// perfect match
|
||||
final GPSFixDTO fixAfter = competitorFixes.get(i);
|
||||
final GPSFixDTOWithSpeedWindTackAndLegType fixAfter = competitorFixes.get(i);
|
||||
result = fixAfter;
|
||||
}
|
||||
}
|
||||
@@ -2507,7 +2541,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
}
|
||||
for (CompetitorDTO competitor : competitors) {
|
||||
try {
|
||||
GPSFixDTO competitorFix = forMap.getBoatFix(competitor, forMap.timer.getTime());
|
||||
GPSFixDTOWithSpeedWindTackAndLegType competitorFix = forMap.getBoatFix(competitor, forMap.timer.getTime());
|
||||
Position competitorPosition = competitorFix != null ? competitorFix.position : null;
|
||||
if (competitorPosition != null) {
|
||||
if (newBounds == null) {
|
||||
@@ -2787,10 +2821,66 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
return null;
|
||||
}
|
||||
|
||||
DelegateCoordinateSystem getCoordinateSystem() {
|
||||
public Map<String, CourseMarkOverlay> getCourseMarkOverlays() {
|
||||
return courseMarkOverlays;
|
||||
}
|
||||
|
||||
public CoordinateSystem getCoordinateSystem() {
|
||||
return coordinateSystem;
|
||||
}
|
||||
|
||||
|
||||
public void hideAllHelplines() {
|
||||
if (startLine != null) {
|
||||
startLine.setVisible(false);
|
||||
}
|
||||
if (finishLine != null) {
|
||||
finishLine.setVisible(false);
|
||||
}
|
||||
if (advantageLine != null) {
|
||||
advantageLine.setVisible(false);
|
||||
}
|
||||
if (windwardStartLineMarkToFirstMarkLine != null && leewardStartLineMarkToFirstMarkLine != null) {
|
||||
windwardStartLineMarkToFirstMarkLine.setVisible(false);
|
||||
leewardStartLineMarkToFirstMarkLine.setVisible(false);
|
||||
}
|
||||
for (Polyline courseMiddleline : courseMiddleLines.values()) {
|
||||
if (courseMiddleline != null) {
|
||||
courseMiddleline.setVisible(false);
|
||||
}
|
||||
}
|
||||
for (Polygon courseSideline : courseSidelines.values()) {
|
||||
if (courseSideline != null) {
|
||||
courseSideline.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void showAllHelplinesToShow() {
|
||||
if (settings.getHelpLinesSettings().isShowAnyHelperLines()) {
|
||||
if (startLine != null && settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE))
|
||||
startLine.setVisible(true);
|
||||
if (finishLine != null && settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE))
|
||||
finishLine.setVisible(true);
|
||||
if (advantageLine != null && settings.getHelpLinesSettings().isVisible(HelpLineTypes.ADVANTAGELINE))
|
||||
advantageLine.setVisible(true);
|
||||
if (windwardStartLineMarkToFirstMarkLine != null && leewardStartLineMarkToFirstMarkLine != null &&
|
||||
settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) {
|
||||
windwardStartLineMarkToFirstMarkLine.setVisible(true);
|
||||
leewardStartLineMarkToFirstMarkLine.setVisible(true);
|
||||
}
|
||||
if (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEMIDDLELINE)) {
|
||||
for (Polyline courseMiddleline : courseMiddleLines.values()) {
|
||||
courseMiddleline.setVisible(true);
|
||||
}
|
||||
}
|
||||
if (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY)) {
|
||||
for (Polygon courseSideline : courseSidelines.values()) {
|
||||
courseSideline.setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setLastInfoWindow(InfoWindow infoWindow) {
|
||||
lastInfoWindow = infoWindow;
|
||||
}
|
||||
|
||||
+3
@@ -649,6 +649,9 @@ public class LeaderboardPanel extends SimplePanel implements Component<Leaderboa
|
||||
protected SailIDColumn(CompetitorFetcher<T> competitorFetcher) {
|
||||
super(new TextCell(), SortingOrder.ASCENDING, LeaderboardPanel.this);
|
||||
this.competitorFetcher = competitorFetcher;
|
||||
// This style is adding to avoid contained images CSS property "max-width: 100%", which could cause
|
||||
// an overflow to the next column (see https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=3537)
|
||||
setCellStyleNames(tableResources.cellTableStyle().cellTableSailIdColumn());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
@@ -149,3 +149,7 @@
|
||||
.cellTableTotalColumn {
|
||||
background-color: #e0e0e0 !important;
|
||||
}
|
||||
|
||||
.cellTableSailIdColumn img {
|
||||
max-width: none;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user