From 6d5f42f75cad94695529db5ed6f65e0f9e088eab Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Mon, 14 Apr 2014 14:44:33 +0200 Subject: [PATCH 01/66] JS cleanup; --- java/com.sap.sailing.gwt.ui/.project | 1 + .../js/wind-streamlets.js | 45 +++++++++---------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/.project b/java/com.sap.sailing.gwt.ui/.project index 0e39cc17829..3093455dba8 100755 --- a/java/com.sap.sailing.gwt.ui/.project +++ b/java/com.sap.sailing.gwt.ui/.project @@ -35,5 +35,6 @@ org.eclipse.pde.PluginNature org.eclipse.jdt.core.javanature com.google.gwt.eclipse.core.gwtNature + org.eclipse.wst.jsdt.core.jsNature diff --git a/java/com.sap.sailing.gwt.ui/js/wind-streamlets.js b/java/com.sap.sailing.gwt.ui/js/wind-streamlets.js index 09ad40871f6..cdec8a2790e 100644 --- a/java/com.sap.sailing.gwt.ui/js/wind-streamlets.js +++ b/java/com.sap.sailing.gwt.ui/js/wind-streamlets.js @@ -108,8 +108,8 @@ Mercator.prototype.calibrate = function() { Mercator.prototype.sphere2plane = function(lat, lng) { var p = lng*Math.PI/180.0; - var latrad = lat*Math.PI/180.0; - var q = Math.log((1.0+Math.sin(latrad))/Math.cos(latrad)); + var latsin = Math.sin(lat*Math.PI/180.0); + var q = 0.5*Math.log((1.0+latsin)/(1.0-latsin)); return {x:p, y:q}; }; @@ -186,7 +186,7 @@ VectorField.prototype.getRandomPosition = function() { var latP = this.bdA.lat + rndY * this.bdB.lat + rndX * this.bdC.lat; var lngP = this.bdA.lng + rndY * this.bdB.lng + rndX * this.bdC.lng; - if (!this.inBounds(lngP, latP)) { + if (swarmDebug&&(!this.inBounds(lngP, latP))) { console.log("random-position: out of bounds"); } @@ -203,9 +203,8 @@ VectorField.prototype.inBounds = function(lng, lat) { VectorField.prototype.interpolate = function(lat, lng) { var idx = this.getNeighbors(lat, lng); - var idxOff = 0; - if ((idx.xTop >= (this.resX+2*this.borderX))||(idx.yTop >= (this.resY+2*this.borderY))) { + if (swarmDebug&&((idx.xTop >= (this.resX+2*this.borderX))||(idx.yTop >= (this.resY+2*this.borderY)))) { console.log("interpolate: out of range: " + idx.xTop + " " + idx.yTop); } @@ -220,9 +219,8 @@ VectorField.prototype.interpolate = function(lat, lng) { VectorField.prototype.interpolate2 = function(lat, lng) { var idx = this.getNeighbors(lat, lng); - var idxOff = 0; - if ((idx.xTop >= (this.resX+2*this.borderX))||(idx.yTop >= (this.resY+2*this.borderY))) { + if (swarmDebug&&((idx.xTop >= (this.resX+2*this.borderX))||(idx.yTop >= (this.resY+2*this.borderY)))) { console.log("interpolate: out of range: " + idx.xTop + " " + idx.yTop); } @@ -246,19 +244,19 @@ VectorField.prototype.setStep = function(step) { } else { this.step = step; } -} +}; VectorField.prototype.nextStep = function() { if (this.step < (this.data.data.length-1)) { this.step++; } -} +}; VectorField.prototype.prevStep = function() { if (this.step > 0) { this.step--; } -} +}; VectorField.prototype.getValue = function(lat, lng, opt_result) { @@ -334,7 +332,7 @@ VectorField.prototype.motionScale = function(zoomLevel) { VectorField.prototype.particleWeight = function(p,v) { return v.length() / this.maxLength + 0.1; -} +}; VectorField.prototype.getColors = function() { var colors = []; @@ -345,11 +343,11 @@ VectorField.prototype.getColors = function() { //this.colors[i] = 'hsla(' + 360*(0.55+0.9*(0.5-i/255)) + ',' + (100) + '% ,' + (50) + '%,' + (i/255) + ')'; } return colors; -} +}; VectorField.prototype.lineWidth = function(s) { return 1.0; -} +}; /** * for compatibility with vector fields aligned with lat/lng-grid @@ -458,7 +456,7 @@ RectField.prototype.motionScale = function(zoomLevel) { RectField.prototype.particleWeight = function(p,v) { return 1.0 - v.length() / this.maxLength; -} +}; RectField.prototype.getColors = function() { var colors = []; @@ -469,11 +467,11 @@ RectField.prototype.getColors = function() { //this.colors[i] = 'hsla(' + 360*(0.55+0.9*(0.5-i/255)) + ',' + (100) + '% ,' + (50) + '%,' + (i/255) + ')'; } return colors; -} +}; RectField.prototype.lineWidth = function(s) { return 1.0; -} +}; var Animator = function() { this.animating = true; @@ -640,7 +638,9 @@ Swarm.prototype.updateBounds = function() { this.numParticles = Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.numParticleFactor; //this.numParticles = Math.sqrt(boundsWidthpx*boundsWidthpx + boundsHeightpx*boundsHeightpx) * this.field.numParticleFactor; - console.log("numParticles: "+this.numParticles + " at " + (boundsWidthpx) +"x" + (boundsHeightpx) + "px (" + (boundsWidthpx * boundsHeightpx) + " pixels)"); + if (swarmDebug) { + console.log("numParticles: "+this.numParticles + " at " + (boundsWidthpx) +"x" + (boundsHeightpx) + "px (" + (boundsWidthpx * boundsHeightpx) + " pixels)"); + } }; Swarm.prototype.isVisible = function(pos) { @@ -772,6 +772,7 @@ var swarmData; var swarmField; var swarmAnimator; var swarmProjection; +var swarmDebug = false; function initStreamlets(map, canvas) { @@ -805,11 +806,7 @@ function initStreamlets(map, canvas) { }; - swarmProjection = new Mercator(map, canvas); //, field); - - //var isMacFF = navigator.platform.indexOf('Mac') != -1 && navigator.userAgent.indexOf('Firefox') != -1; - var isWinFF = navigator.platform.indexOf('Win') != -1 && navigator.userAgent.indexOf('Firefox') != -1; - var isWinIE = navigator.platform.indexOf('Win') != -1 && navigator.userAgent.indexOf('MSIE') != -1; + swarmProjection = new Mercator(map, canvas); var swarm = new Swarm(canvas, swarmField, swarmProjection); @@ -818,7 +815,7 @@ function initStreamlets(map, canvas) { updateStreamlets = function(upd_data) { if (!(upd_data === undefined)) { - swarmAnimator.state = 'stop' + swarmAnimator.state = 'stop'; swarmAnimator = new Animator(); swarmField = new VectorField(windFieldSim); swarm = new Swarm(canvas, swarmField, swarmProjection); @@ -836,7 +833,7 @@ function initStreamlets(map, canvas) { stopStreamlets = function() { swarmAnimator.state = 'stop'; swarmProjection.clearCanvas(); - } + }; if (swarmProjection.fieldNW === undefined) { google.maps.event.addListener(map, 'bounds_changed', updateStreamlets); From 67a58d56cae00d22c6fcdcf1148935a672a44b04 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Wed, 16 Apr 2014 18:22:22 +0200 Subject: [PATCH 02/66] fix URL-parameter interpretation for border=0; --- .../com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java index 9cc695c7fdc..641ca389989 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java @@ -132,7 +132,7 @@ public class SimulatorEntryPoint extends AbstractEntryPoint { showStreamlets2 = true; injectWindDataJS = true; } - if ((showStreamlets2)&&(this.border==0)) { + if ((showStreamlets2)&&(border==null)) { this.border = 10; } if (windDisplayStr.contains("b")) { From 326641bf05579ac09607e45ab1c5ad0114693381 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Wed, 23 Apr 2014 11:24:34 +0200 Subject: [PATCH 03/66] test implementation GWT streamlets; --- .../sailing/gwt/ui/simulator/Mercator.java | 86 ++++++++++ .../sailing/gwt/ui/simulator/Particle.java | 13 ++ .../sailing/gwt/ui/simulator/RectField.java | 159 ++++++++++++++++++ .../gwt/ui/simulator/SimulatorEntryPoint.java | 9 +- .../gwt/ui/simulator/SimulatorMainPanel.java | 8 +- .../WindStreamletsCanvasOverlay.java | 125 +++++++++++++- 6 files changed, 394 insertions(+), 6 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Mercator.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Mercator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Mercator.java new file mode 100644 index 00000000000..dcbcdda94fd --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Mercator.java @@ -0,0 +1,86 @@ +package com.sap.sailing.gwt.ui.simulator; + +import com.google.gwt.canvas.dom.client.Context2d; +import com.google.gwt.maps.client.MapWidget; +import com.google.gwt.maps.client.base.LatLng; +import com.google.gwt.maps.client.base.Point; +import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; + +public class Mercator { + + private FullCanvasOverlay canvas; + private MapWidget map; + + private double alpha; + private double beta; + private double gamma; + private double delta; + + public Mercator(FullCanvasOverlay canvas, MapWidget map) { + this.canvas = canvas; + this.alpha = 0.0; + this.beta = 0.0; + this.gamma = 0.0; + this.delta = 0.0; + this.map = map; + this.calibrate(); + } + + public void calibrate() { + + Point pointSW; + Point pointNE; + + int canvasHeight = canvas.getCanvas().getOffsetHeight(); + + LatLng mapSW = this.map.getBounds().getSouthWest(); + LatLng mapNE = this.map.getBounds().getNorthEast(); + + pointSW = this.sphere2plane(mapSW); + pointNE = this.sphere2plane(mapNE); + + if (pointNE.getX() < pointSW.getX()) { + pointSW = Point.newInstance(pointSW.getX() - 2*Math.PI, pointSW.getY()); + } + + this.alpha = canvasHeight / (pointNE.getY() - pointSW.getY()); + this.beta = pointSW.getX(); + + this.gamma = - this.alpha; + this.delta = pointNE.getY(); + } + + public Point sphere2plane(LatLng p) { + double x = p.getLongitude()*Math.PI/180.0; + double latsin = Math.sin(p.getLatitude()*Math.PI/180.0); + double y = 0.5*Math.log((1.0+latsin)/(1.0-latsin)); + return Point.newInstance(x, y); + } + + public LatLng plane2sphere(Point px) { + double lng = px.getX()*180.0/Math.PI; + double lat = Math.atan(Math.sinh(px.getY()))*180.0/Math.PI; + return LatLng.newInstance(lat, lng); + } + + public Point latlng2pixel(LatLng p) { + Point proj = this.sphere2plane(p); + double x = this.alpha * (proj.getX() - this.beta); + double y = this.gamma * (proj.getY() - this.delta); + return Point.newInstance(x, y); + } + + public LatLng pixel2latlng(Point px) { + double p = px.getX() / this.alpha + this.beta; + double q = px.getY() / this.gamma + this.delta; + return this.plane2sphere(Point.newInstance(p, q)); + } + + public void clearCanvas() { + double w = this.canvas.getCanvas().getOffsetWidth(); + double h = this.canvas.getCanvas().getOffsetHeight(); + Context2d g = this.canvas.getCanvas().getContext2d(); + g.clearRect(0, 0, w, h); + }; + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java new file mode 100644 index 00000000000..a45bf57077c --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java @@ -0,0 +1,13 @@ +package com.sap.sailing.gwt.ui.simulator; + +import com.google.gwt.maps.client.base.LatLng; +import com.google.gwt.maps.client.base.Point; + +public class Particle { + + public LatLng pos; + public Point pxOld; + public int alpha; + public int age; + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java new file mode 100644 index 00000000000..d6898de4701 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java @@ -0,0 +1,159 @@ +package com.sap.sailing.gwt.ui.simulator; + +import com.google.gwt.json.client.JSONObject; +import com.google.gwt.json.client.JSONParser; +import com.google.gwt.maps.client.base.LatLng; +import com.google.gwt.maps.client.base.Point; + +public class RectField { + + Point[][] field; + + private double x0; + private double x1; + private double y0; + private double y1; + + private double visX0; + private double visX1; + private double visY0; + private double visY1; + + private int w; + private int h; + + private double maxLength; + //private double numParticleFactor; + + public RectField(Point[][] field, double x0, double y0, double x1, double y1) { + this.x0 = x0; + this.x1 = x1; + this.y0 = y0; + this.y1 = y1; + + this.visX0 = x0; + this.visY0 = y0; + this.visX1 = x1; + this.visY1 = y1; + + this.field = field; + this.w = field.length; + this.h = field[0].length; + this.maxLength = 0; + //this.numParticleFactor = 4.5; + + double mx = 0; + double my = 0; + for (int i = 0; i < this.w; i++) { + for (int j = 0; j < this.h; j++) { + if (length(field[i][j]) > this.maxLength) { + mx = i; + my = j; + } + this.maxLength = Math.max(this.maxLength, length(field[i][j])); + } + } + mx = (mx / this.w) * (x1 - x0) + x0; + my = (my / this.h) * (y1 - y0) + y0; + } + + public static double length(Point p) { + return Math.sqrt(p.getX()*p.getX() + p.getY()*p.getY()); + } + + public static RectField read(String jsonData, boolean correctForSphere) { + JSONObject data = JSONParser.parseLenient(jsonData).isObject(); + int w = (int)data.get("gridWidth").isNumber().doubleValue(); + int h = (int)data.get("gridHeight").isNumber().doubleValue(); + Point[][] field = new Point[w][h]; + + int i = 0; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + double vx = data.get("field").isArray().get(i++).isNumber().doubleValue(); + double vy = data.get("field").isArray().get(i++).isNumber().doubleValue(); + Point v = Point.newInstance(vx, vy); + if (correctForSphere) { + double uy = y / (h - 1); + double lat = data.get("y0").isNumber().doubleValue() * (1 - uy) + data.get("y1").isNumber().doubleValue() * uy; + double m = Math.PI * lat / 180; + //double length = length(v); + v = Point.newInstance(v.getX() / Math.cos(m), v.getY()); + //v.setLength(length); + } + field[x][y] = v; + } + } + RectField result = new RectField(field, data.get("x0").isNumber().doubleValue(), data.get("y0").isNumber().doubleValue(), data.get("x1").isNumber().doubleValue(), data.get("y1").isNumber().doubleValue()); + return result; + } + + public LatLng getRandomPosition() { + double rndY = Math.random(); + double rndX = Math.random(); + double y = rndY * this.visY0 + (1 - rndY) * this.visY1; + double x = rndX * this.visX0 + (1 - rndX) * this.visX1; + return LatLng.newInstance(y, x); + } + + public boolean inBounds(double x, double y) { + return x >= this.x0 && x < this.x1 && y >= this.y0 && y < this.y1; + } + + public Point interpolate(double a, double b) { + int na = (int)Math.floor(a); + int nb = (int)Math.floor(b); + int ma = (int)Math.ceil(a); + int mb = (int)Math.ceil(b); + double fa = a - na; + double fb = b - nb; + + double avgX = this.field[na][nb].getX() * (1 - fa) * (1 - fb) + this.field[ma][nb].getX() * fa * (1 - fb) + this.field[na][mb].getX() * (1 - fa) * fb + this.field[ma][mb].getX() * fa * fb; + double avgY = this.field[na][nb].getY() * (1 - fa) * (1 - fb) + this.field[ma][nb].getY() * fa * (1 - fb) + this.field[na][mb].getY() * (1 - fa) * fb + this.field[ma][mb].getY() * fa * fb; + + return Point.newInstance(avgX, avgY); + }; + + + public Point getValue(LatLng p) { + double a = (this.w - 1 - 1e-6) * (p.getLongitude() - this.x0) / (this.x1 - this.x0); + double b = (this.h - 1 - 1e-6) * (p.getLatitude() - this.y0) / (this.y1 - this.y0); + if ((a < 0)||(a > (this.w-1))||(b < 0)||(b > (this.h-1))) { + return null; + } else { + return this.interpolate(a, b); + } + }; + + public LatLng getCenter() { + return LatLng.newInstance((y0+y1)/2.0, (x0+x1)/2.0); + } + + public double getMaxLength() { + return maxLength; + } + + /*RectField.prototype.motionScale = function(zoomLevel) { + return 0.9 * Math.pow(1.7, Math.min(1.0, 6.0 - zoomLevel)); + }; + + RectField.prototype.particleWeight = function(p,v) { + return 1.0 - v.length() / this.maxLength; + }; + + RectField.prototype.getColors = function() { + var colors = []; + var alpha = 1.0; + var greyValue = 255; + for (var i = 0; i < 256; i++) { + colors[i] = 'rgba(' + (greyValue) + ',' + (greyValue) + ',' + (greyValue) + ',' + (alpha*i/255.0) + ')'; + //this.colors[i] = 'hsla(' + 360*(0.55+0.9*(0.5-i/255)) + ',' + (100) + '% ,' + (50) + '%,' + (i/255) + ')'; + } + return colors; + }; + + RectField.prototype.lineWidth = function(s) { + return 1.0; + };*/ + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java index 641ca389989..042381ef862 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java @@ -25,6 +25,7 @@ public class SimulatorEntryPoint extends AbstractEntryPoint { private int xRes = 40; private int yRes = 20; private int border = 0; + private int particles = 0; private boolean autoUpdate = false; private char mode = SailingSimulatorConstants.ModeEvent; // default mode: 'e'vent private char event = SailingSimulatorConstants.EventKielerWoche; // default event: 'k'ieler woche @@ -75,6 +76,12 @@ public class SimulatorEntryPoint extends AbstractEntryPoint { } else { this.border = Integer.parseInt(border); } + String particlesStr = Window.Location.getParameter("particles"); + if (particlesStr == null || particlesStr.isEmpty()) { + logger.config("Using default particles " + this.particles); + } else { + this.particles = Integer.parseInt(particlesStr); + } String autoUpdateStr = Window.Location.getParameter("autoUpdate"); if (autoUpdateStr == null || autoUpdateStr.isEmpty()) { logger.config("Using default auto update " + autoUpdate); @@ -163,7 +170,7 @@ public class SimulatorEntryPoint extends AbstractEntryPoint { } private void createSimulatorPanel() { - SimulatorMainPanel simulatorPanel = new SimulatorMainPanel(simulatorService, stringMessages, this, xRes, yRes, border, + SimulatorMainPanel simulatorPanel = new SimulatorMainPanel(simulatorService, stringMessages, this, xRes, yRes, border, particles, autoUpdate, mode, event, showGrid, showLines, seedLines, showArrows, showStreamlets, showStreamlets2, injectWindDataJS); DockLayoutPanel p = new DockLayoutPanel(Unit.PX); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMainPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMainPanel.java index df5690e857e..773ef8c98ff 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMainPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMainPanel.java @@ -71,7 +71,9 @@ public class SimulatorMainPanel extends SimplePanel { simulatorMap.getMap().triggerResize(); } } - + + public int particles; + private DockLayoutPanel mainPanel; private FlowPanel leftPanel; private ResizableFlowPanel rightPanel; @@ -225,10 +227,12 @@ public class SimulatorMainPanel extends SimplePanel { return NumberFormat.getFormat("0.0").format(value); } - public SimulatorMainPanel(SimulatorServiceAsync svc, StringMessages stringMessages, ErrorReporter errorReporter, int xRes, int yRes, int border, boolean autoUpdate, + public SimulatorMainPanel(SimulatorServiceAsync svc, StringMessages stringMessages, ErrorReporter errorReporter, int xRes, int yRes, int border, int particles, boolean autoUpdate, char mode, char event, boolean showGrid, boolean showLines, char seedLines, boolean showArrows, boolean showStreamlets, boolean showStreamlets2, boolean injectWindDataJS) { super(); + this.particles = particles; + this.simulatorSvc = svc; this.stringMessages = stringMessages; this.errorReporter = errorReporter; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index 513b95d1e52..991b5bd3ec2 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -4,12 +4,16 @@ import java.util.Date; import java.util.List; import java.util.logging.Logger; +import com.google.gwt.canvas.dom.client.Context2d; +import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.core.client.Scheduler; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONNumber; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONString; import com.google.gwt.maps.client.base.LatLng; +import com.google.gwt.maps.client.base.Point; import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO; @@ -23,7 +27,7 @@ import com.sap.sse.gwt.client.player.Timer; * @author Nidhi Sawhney(D054070) * */ -public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements TimeListenerWithStoppingCriteria { +public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements TimeListenerWithStoppingCriteria, Scheduler.RepeatingCommand { /** The wind field that is to be displayed in the overlay */ protected WindFieldDTO windFieldDTO; @@ -32,6 +36,14 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti private boolean visible = false; private Timer timer; + + private String[] color; + private Mercator projection; + private RectField field; + private int nParticles; + private Particle[] particles; + private LatLng pNE; + private LatLng pSW; private static Logger logger = Logger.getLogger(WindStreamletsCanvasOverlay.class.getName()); @@ -42,6 +54,9 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti this.timer = timer; this.windParams = windParams; + this.nParticles = this.simulatorMap.getMainPanel().particles; + + windFieldDTO = null; getCanvas().getElement().setId("swarm-display"); @@ -254,11 +269,115 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti @Override protected void draw() { super.draw(); - if (mapProjection != null && windFieldDTO != null) { - // drawing is done by external JavaScript for Streamlets + if (mapProjection != null) { + if ((nParticles > 0)&&(projection == null)) { + this.gwtSwarmTest(); + } + if (windFieldDTO != null) { + // drawing is done by external JavaScript for Streamlets + } } } + + public void gwtSwarmTest() { + + projection = new Mercator(this, map); + + if (field == null) { + SimulatorJSBundle bundle = GWT.create(SimulatorJSBundle.class); + String jsonStr = bundle.windStreamletsDataJS().getText(); + field = RectField.read(jsonStr.substring(19, jsonStr.length()-1), false); + + map.setZoom(5); + map.panTo(field.getCenter()); + + projection.calibrate(); + } + + color = new String[256]; + for(int alpha=0; alpha<=255; alpha++) { + color[alpha] = "rgba(255,255,255,"+(((double)alpha)/255.0)+")"; + } + + Context2d ctxt = canvas.getContext2d(); + ctxt.setFillStyle("white"); + //ctxt.fillRect(100, 100, 300, 300); + + particles = new Particle[nParticles]; + pNE = map.getBounds().getNorthEast(); + pSW = map.getBounds().getSouthWest(); + + for(int idx=0; idx 0) { + Point v = field.getValue(particles[idx].pos); + if (v == null) { + particles[idx].age = 0; + } else { + //p[idx].pos = LatLng.newInstance(p[idx].pos.getLatitude()+off, p[idx].pos.getLongitude()+off); + double lat = particles[idx].pos.getLatitude() + off*v.getY(); + double lng = particles[idx].pos.getLongitude() + off*v.getX(); + double s = RectField.length(v) / field.getMaxLength(); + particles[idx].alpha = (int)Math.min(255, 90 + Math.round(350 * s)); + particles[idx].pos = LatLng.newInstance(lat, lng); + particles[idx].age--; + } + } + + } + + drawSwarm(); + //System.out.println("loop execute"); + return true; + } + private void clear() { this.stopStreamlets(); } From ee62bea6e7c5bf114934f9491263efed714425d7 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Wed, 23 Apr 2014 11:31:06 +0200 Subject: [PATCH 04/66] some formatting & cleanup; --- .../WindStreamletsCanvasOverlay.java | 71 +++++++++---------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index 991b5bd3ec2..fda9e26ab14 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -280,29 +280,28 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti } public void gwtSwarmTest() { - + projection = new Mercator(this, map); - + if (field == null) { SimulatorJSBundle bundle = GWT.create(SimulatorJSBundle.class); String jsonStr = bundle.windStreamletsDataJS().getText(); field = RectField.read(jsonStr.substring(19, jsonStr.length()-1), false); - + map.setZoom(5); - map.panTo(field.getCenter()); - - projection.calibrate(); + map.panTo(field.getCenter()); + + projection.calibrate(); } - + color = new String[256]; for(int alpha=0; alpha<=255; alpha++) { color[alpha] = "rgba(255,255,255,"+(((double)alpha)/255.0)+")"; } - + Context2d ctxt = canvas.getContext2d(); ctxt.setFillStyle("white"); - //ctxt.fillRect(100, 100, 300, 300); - + particles = new Particle[nParticles]; pNE = map.getBounds().getNorthEast(); @@ -320,10 +319,10 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti Scheduler scheduler = Scheduler.get(); scheduler.scheduleFixedPeriod(this, 40); } - + public void drawSwarm() { Context2d ctxt = canvas.getContext2d(); - + ctxt.setGlobalAlpha(0.05); ctxt.setGlobalCompositeOperation("destination-out"); ctxt.setFillStyle("black"); @@ -335,49 +334,47 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti if (particles[idx].age == 0) { continue; } - //ctxt.fillRect(x.getX(), x.getY(), 2, 2); ctxt.setLineWidth(1.0); ctxt.setStrokeStyle(color[particles[idx].alpha]); - ctxt.beginPath(); - ctxt.moveTo(particles[idx].pxOld.getX(), particles[idx].pxOld.getY()); + ctxt.beginPath(); + ctxt.moveTo(particles[idx].pxOld.getX(), particles[idx].pxOld.getY()); particles[idx].pxOld = projection.latlng2pixel(particles[idx].pos); - ctxt.lineTo(particles[idx].pxOld.getX(), particles[idx].pxOld.getY()); - ctxt.stroke(); + ctxt.lineTo(particles[idx].pxOld.getX(), particles[idx].pxOld.getY()); + ctxt.stroke(); } } - + public boolean execute() { double off = 0.01; for(int idx=0; idx 0) { - Point v = field.getValue(particles[idx].pos); - if (v == null) { - particles[idx].age = 0; - } else { - //p[idx].pos = LatLng.newInstance(p[idx].pos.getLatitude()+off, p[idx].pos.getLongitude()+off); - double lat = particles[idx].pos.getLatitude() + off*v.getY(); - double lng = particles[idx].pos.getLongitude() + off*v.getX(); - double s = RectField.length(v) / field.getMaxLength(); - particles[idx].alpha = (int)Math.min(255, 90 + Math.round(350 * s)); - particles[idx].pos = LatLng.newInstance(lat, lng); - particles[idx].age--; + Point v = field.getValue(particles[idx].pos); + if (v == null) { + particles[idx].age = 0; + } else { + double lat = particles[idx].pos.getLatitude() + off*v.getY(); + double lng = particles[idx].pos.getLongitude() + off*v.getX(); + double s = RectField.length(v) / field.getMaxLength(); + particles[idx].alpha = (int)Math.min(255, 90 + Math.round(350 * s)); + particles[idx].pos = LatLng.newInstance(lat, lng); + particles[idx].age--; + } } - } - + } - + drawSwarm(); - //System.out.println("loop execute"); + return true; } - + private void clear() { this.stopStreamlets(); } From e4ff29f52b2efbd43615bbae11e9e5d66e78034e Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Wed, 23 Apr 2014 19:13:16 +0200 Subject: [PATCH 05/66] include weighing by vector length; reduce number of vector interpolations; reduce array accesses by index "[idx]"; --- .../sailing/gwt/ui/simulator/Particle.java | 1 + .../sailing/gwt/ui/simulator/RectField.java | 10 +-- .../WindStreamletsCanvasOverlay.java | 67 ++++++++++++------- 3 files changed, 48 insertions(+), 30 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java index a45bf57077c..c9141c98421 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java @@ -9,5 +9,6 @@ public class Particle { public Point pxOld; public int alpha; public int age; + public Point v; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java index d6898de4701..4871f8dae72 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java @@ -135,13 +135,13 @@ public class RectField { /*RectField.prototype.motionScale = function(zoomLevel) { return 0.9 * Math.pow(1.7, Math.min(1.0, 6.0 - zoomLevel)); + };*/ + + public double particleWeight(LatLng p, Point v) { + return 1.0 - length(v) / this.maxLength; }; - RectField.prototype.particleWeight = function(p,v) { - return 1.0 - v.length() / this.maxLength; - }; - - RectField.prototype.getColors = function() { + /*RectField.prototype.getColors = function() { var colors = []; var alpha = 1.0; var greyValue = 255; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index fda9e26ab14..f7bd4ed4904 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -309,11 +309,14 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti for(int idx=0; idx= Math.random())&&(v != null)) { + particle.age = Math.max(2, (int)Math.round(Math.random()*40)); + particle.pxOld = projection.latlng2pixel(particle.pos); + particle.alpha = 0; + particle.v = v; + done = true; + } + } } - if (particles[idx].age > 0) { - Point v = field.getValue(particles[idx].pos); - if (v == null) { - particles[idx].age = 0; + if (particle.age > 0) { + if (particle.v == null) { + particle.age = 0; } else { - double lat = particles[idx].pos.getLatitude() + off*v.getY(); - double lng = particles[idx].pos.getLongitude() + off*v.getX(); - double s = RectField.length(v) / field.getMaxLength(); - particles[idx].alpha = (int)Math.min(255, 90 + Math.round(350 * s)); - particles[idx].pos = LatLng.newInstance(lat, lng); - particles[idx].age--; + double lat = particle.pos.getLatitude() + off*particle.v.getY(); + double lng = particle.pos.getLongitude() + off*particle.v.getX(); + double s = RectField.length(particle.v) / field.getMaxLength(); + particle.alpha = (int)Math.min(255, 90 + Math.round(350 * s)); + particle.pos = LatLng.newInstance(lat, lng); + particle.age--; + if (particle.age > 0) { + particle.v = field.getValue(particle.pos); + } } } From 2e2820aed14699ced3a0302edf2ae744ff794287 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Sat, 26 Apr 2014 00:05:17 +0200 Subject: [PATCH 06/66] fix performance issues by replacing Google Point and LatLng with lean classes Vector and GeoPos; add bounds check; add update logic for Map move & zoom and screen resize; --- .../sap/sailing/gwt/ui/simulator/GeoPos.java | 24 ++ .../sailing/gwt/ui/simulator/Mercator.java | 43 ++-- .../sailing/gwt/ui/simulator/Particle.java | 8 +- .../sailing/gwt/ui/simulator/RectField.java | 109 ++++----- .../gwt/ui/simulator/SimulatorMap.java | 56 ++++- .../sap/sailing/gwt/ui/simulator/Vector.java | 12 + .../WindStreamletsCanvasOverlay.java | 206 +++++++++++++++--- 7 files changed, 342 insertions(+), 116 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/GeoPos.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/GeoPos.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/GeoPos.java new file mode 100644 index 00000000000..e2ab76d3db3 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/GeoPos.java @@ -0,0 +1,24 @@ +package com.sap.sailing.gwt.ui.simulator; + +import com.google.gwt.maps.client.base.LatLng; + +public class GeoPos { + + public double lat; + public double lng; + + public GeoPos() { + + } + + public GeoPos(double lat, double lng) { + this.lat = lat; + this.lng = lng; + } + + public GeoPos(LatLng pos) { + this.lat = pos.getLatitude(); + this.lng = pos.getLongitude(); + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Mercator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Mercator.java index dcbcdda94fd..e57ab0b3087 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Mercator.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Mercator.java @@ -28,33 +28,38 @@ public class Mercator { public void calibrate() { - Point pointSW; - Point pointNE; + Vector pointSW; + Vector pointNE; int canvasHeight = canvas.getCanvas().getOffsetHeight(); - LatLng mapSW = this.map.getBounds().getSouthWest(); - LatLng mapNE = this.map.getBounds().getNorthEast(); + GeoPos mapSW = new GeoPos(); + mapSW.lat = this.map.getBounds().getSouthWest().getLatitude(); + mapSW.lng = this.map.getBounds().getSouthWest().getLongitude(); + GeoPos mapNE = new GeoPos(); + mapNE.lat = this.map.getBounds().getNorthEast().getLatitude(); + mapNE.lng = this.map.getBounds().getNorthEast().getLongitude(); pointSW = this.sphere2plane(mapSW); pointNE = this.sphere2plane(mapNE); - if (pointNE.getX() < pointSW.getX()) { - pointSW = Point.newInstance(pointSW.getX() - 2*Math.PI, pointSW.getY()); + if (pointNE.x < pointSW.x) { + pointSW.x -= 2*Math.PI; } - this.alpha = canvasHeight / (pointNE.getY() - pointSW.getY()); - this.beta = pointSW.getX(); + this.alpha = canvasHeight / (pointNE.y - pointSW.y); + this.beta = pointSW.x; this.gamma = - this.alpha; - this.delta = pointNE.getY(); + this.delta = pointNE.y; } - public Point sphere2plane(LatLng p) { - double x = p.getLongitude()*Math.PI/180.0; - double latsin = Math.sin(p.getLatitude()*Math.PI/180.0); - double y = 0.5*Math.log((1.0+latsin)/(1.0-latsin)); - return Point.newInstance(x, y); + public Vector sphere2plane(GeoPos p) { + Vector result = new Vector(); + result.x = p.lng*Math.PI/180.0; + double latsin = Math.sin(p.lat*Math.PI/180.0); + result.y = 0.5*Math.log((1.0+latsin)/(1.0-latsin)); + return result; } public LatLng plane2sphere(Point px) { @@ -63,11 +68,11 @@ public class Mercator { return LatLng.newInstance(lat, lng); } - public Point latlng2pixel(LatLng p) { - Point proj = this.sphere2plane(p); - double x = this.alpha * (proj.getX() - this.beta); - double y = this.gamma * (proj.getY() - this.delta); - return Point.newInstance(x, y); + public Vector latlng2pixel(GeoPos p) { + Vector proj = this.sphere2plane(p); + proj.x = this.alpha * (proj.x - this.beta); + proj.y = this.gamma * (proj.y - this.delta); + return proj; } public LatLng pixel2latlng(Point px) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java index c9141c98421..79d9e6de302 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java @@ -1,14 +1,12 @@ package com.sap.sailing.gwt.ui.simulator; -import com.google.gwt.maps.client.base.LatLng; -import com.google.gwt.maps.client.base.Point; public class Particle { - public LatLng pos; - public Point pxOld; + public GeoPos pos; + public Vector pxOld; public int alpha; public int age; - public Point v; + public Vector v; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java index 4871f8dae72..6048b912abb 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java @@ -3,82 +3,81 @@ package com.sap.sailing.gwt.ui.simulator; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONParser; import com.google.gwt.maps.client.base.LatLng; -import com.google.gwt.maps.client.base.Point; public class RectField { - Point[][] field; + Vector[][] field; - private double x0; - private double x1; - private double y0; - private double y1; + public double x0; + public double x1; + public double y0; + public double y1; - private double visX0; - private double visX1; - private double visY0; - private double visY1; + public double visX0; + public double visX1; + public double visY0; + public double visY1; private int w; private int h; private double maxLength; - //private double numParticleFactor; + public double numParticleFactor; - public RectField(Point[][] field, double x0, double y0, double x1, double y1) { + public RectField(Vector[][] field, double x0, double y0, double x1, double y1) { this.x0 = x0; this.x1 = x1; this.y0 = y0; this.y1 = y1; - this.visX0 = x0; - this.visY0 = y0; - this.visX1 = x1; - this.visY1 = y1; + this.visX0 = 0; + this.visY0 = 0; + this.visX1 = 0; + this.visY1 = 0; this.field = field; this.w = field.length; this.h = field[0].length; this.maxLength = 0; - //this.numParticleFactor = 4.5; + this.numParticleFactor = 4.5; double mx = 0; double my = 0; for (int i = 0; i < this.w; i++) { for (int j = 0; j < this.h; j++) { - if (length(field[i][j]) > this.maxLength) { + if (field[i][j].length() > this.maxLength) { mx = i; my = j; } - this.maxLength = Math.max(this.maxLength, length(field[i][j])); + this.maxLength = Math.max(this.maxLength, field[i][j].length()); } } mx = (mx / this.w) * (x1 - x0) + x0; my = (my / this.h) * (y1 - y0) + y0; } - public static double length(Point p) { - return Math.sqrt(p.getX()*p.getX() + p.getY()*p.getY()); - } + //public static double length(Vector p) { + // return Math.sqrt(p.x*p.x + p.y*p.y); + //} public static RectField read(String jsonData, boolean correctForSphere) { JSONObject data = JSONParser.parseLenient(jsonData).isObject(); int w = (int)data.get("gridWidth").isNumber().doubleValue(); int h = (int)data.get("gridHeight").isNumber().doubleValue(); - Point[][] field = new Point[w][h]; + Vector[][] field = new Vector[w][h]; int i = 0; for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { - double vx = data.get("field").isArray().get(i++).isNumber().doubleValue(); - double vy = data.get("field").isArray().get(i++).isNumber().doubleValue(); - Point v = Point.newInstance(vx, vy); + Vector v = new Vector(); + v.x = data.get("field").isArray().get(i++).isNumber().doubleValue(); + v.y = data.get("field").isArray().get(i++).isNumber().doubleValue(); if (correctForSphere) { double uy = y / (h - 1); double lat = data.get("y0").isNumber().doubleValue() * (1 - uy) + data.get("y1").isNumber().doubleValue() * uy; double m = Math.PI * lat / 180; //double length = length(v); - v = Point.newInstance(v.getX() / Math.cos(m), v.getY()); + v.x = v.x / Math.cos(m); //v.setLength(length); } field[x][y] = v; @@ -88,19 +87,20 @@ public class RectField { return result; } - public LatLng getRandomPosition() { + public GeoPos getRandomPosition() { double rndY = Math.random(); double rndX = Math.random(); - double y = rndY * this.visY0 + (1 - rndY) * this.visY1; - double x = rndX * this.visX0 + (1 - rndX) * this.visX1; - return LatLng.newInstance(y, x); + GeoPos result = new GeoPos(); + result.lat = rndY * this.visY0 + (1 - rndY) * this.visY1; + result.lng = rndX * this.visX0 + (1 - rndX) * this.visX1; + return result; } public boolean inBounds(double x, double y) { return x >= this.x0 && x < this.x1 && y >= this.y0 && y < this.y1; } - public Point interpolate(double a, double b) { + public Vector interpolate(double a, double b) { int na = (int)Math.floor(a); int nb = (int)Math.floor(b); int ma = (int)Math.ceil(a); @@ -108,22 +108,23 @@ public class RectField { double fa = a - na; double fb = b - nb; - double avgX = this.field[na][nb].getX() * (1 - fa) * (1 - fb) + this.field[ma][nb].getX() * fa * (1 - fb) + this.field[na][mb].getX() * (1 - fa) * fb + this.field[ma][mb].getX() * fa * fb; - double avgY = this.field[na][nb].getY() * (1 - fa) * (1 - fb) + this.field[ma][nb].getY() * fa * (1 - fb) + this.field[na][mb].getY() * (1 - fa) * fb + this.field[ma][mb].getY() * fa * fb; + Vector result = new Vector(); + result.x = this.field[na][nb].x * (1 - fa) * (1 - fb) + this.field[ma][nb].x * fa * (1 - fb) + this.field[na][mb].x * (1 - fa) * fb + this.field[ma][mb].x * fa * fb; + result.y = this.field[na][nb].y * (1 - fa) * (1 - fb) + this.field[ma][nb].y * fa * (1 - fb) + this.field[na][mb].y * (1 - fa) * fb + this.field[ma][mb].y * fa * fb; - return Point.newInstance(avgX, avgY); - }; + return result; + } - public Point getValue(LatLng p) { - double a = (this.w - 1 - 1e-6) * (p.getLongitude() - this.x0) / (this.x1 - this.x0); - double b = (this.h - 1 - 1e-6) * (p.getLatitude() - this.y0) / (this.y1 - this.y0); + public Vector getVector(GeoPos p) { + double a = (this.w - 1 - 1e-6) * (p.lng - this.x0) / (this.x1 - this.x0); + double b = (this.h - 1 - 1e-6) * (p.lat - this.y0) / (this.y1 - this.y0); if ((a < 0)||(a > (this.w-1))||(b < 0)||(b > (this.h-1))) { return null; } else { return this.interpolate(a, b); } - }; + } public LatLng getCenter() { return LatLng.newInstance((y0+y1)/2.0, (x0+x1)/2.0); @@ -133,27 +134,27 @@ public class RectField { return maxLength; } - /*RectField.prototype.motionScale = function(zoomLevel) { + public double motionScale(int zoomLevel) { return 0.9 * Math.pow(1.7, Math.min(1.0, 6.0 - zoomLevel)); - };*/ + } - public double particleWeight(LatLng p, Point v) { - return 1.0 - length(v) / this.maxLength; - }; + public double particleWeight(GeoPos p, Vector v) { + return 1.0 - v.length() / this.maxLength; + } - /*RectField.prototype.getColors = function() { - var colors = []; - var alpha = 1.0; - var greyValue = 255; - for (var i = 0; i < 256; i++) { - colors[i] = 'rgba(' + (greyValue) + ',' + (greyValue) + ',' + (greyValue) + ',' + (alpha*i/255.0) + ')'; + public String[] getColors() { + String[] colors = new String[256]; + double alpha = 1.0; + int greyValue = 255; + for (int i = 0; i < 256; i++) { + colors[i] = "rgba(" + (greyValue) + "," + (greyValue) + "," + (greyValue) + "," + (alpha*i/255.0) + ")"; //this.colors[i] = 'hsla(' + 360*(0.55+0.9*(0.5-i/255)) + ',' + (100) + '% ,' + (50) + '%,' + (i/255) + ')'; } return colors; - }; + } - RectField.prototype.lineWidth = function(s) { + public double lineWidth(int alpha) { return 1.0; - };*/ + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java index 3d64dd48890..1a37639f944 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.logging.Logger; import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.Scheduler; import com.google.gwt.event.logical.shared.ResizeEvent; import com.google.gwt.event.logical.shared.ResizeHandler; import com.google.gwt.maps.client.LoadApi; @@ -19,6 +20,8 @@ import com.google.gwt.maps.client.controls.MapTypeStyle; import com.google.gwt.maps.client.controls.PanControlOptions; import com.google.gwt.maps.client.controls.ScaleControlOptions; import com.google.gwt.maps.client.controls.ZoomControlOptions; +import com.google.gwt.maps.client.events.bounds.BoundsChangeMapEvent; +import com.google.gwt.maps.client.events.bounds.BoundsChangeMapHandler; 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.idle.IdleMapEvent; @@ -369,7 +372,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ if (windParams.isShowStreamlets2()) { - mapTypeStyles = new MapTypeStyle[7]; + mapTypeStyles = new MapTypeStyle[11]; // hide all transit lines including ferry lines mapTypeStyles[0] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.TRANSIT); @@ -379,18 +382,45 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ mapTypeStyles[2] = GoogleMapStyleHelper.createSimplifiedStyle(MapTypeStyleFeatureType.ROAD); // set water color mapTypeStyles[3] = GoogleMapStyleHelper.createColorStyle(MapTypeStyleFeatureType.WATER, new RGBColor(0, 136, 255), -50, -50); - mapTypeStyles[4] = GoogleMapStyleHelper.createColorStyle(MapTypeStyleFeatureType.LANDSCAPE, new RGBColor(255, 255, 255), 0, -70); - mapTypeStyles[5] = GoogleMapStyleHelper.createElementStyleOnlyLightness(MapTypeStyleFeatureType.ROAD, MapTypeStyleElementType.ALL, -40); + mapTypeStyles[4] = GoogleMapStyleHelper.createColorStyle(MapTypeStyleFeatureType.LANDSCAPE, new RGBColor(255, 255, 255), -100, -70); + mapTypeStyles[5] = GoogleMapStyleHelper.createColorStyle(MapTypeStyleFeatureType.POI, new RGBColor(255, 255, 255), -100, -70); + mapTypeStyles[6] = GoogleMapStyleHelper.createElementStyleOnlyLightness(MapTypeStyleFeatureType.ROAD, MapTypeStyleElementType.ALL, -40); MapTypeStyle mapStyle = MapTypeStyle.newInstance(); mapStyle.setFeatureType(MapTypeStyleFeatureType.ADMINISTRATIVE); - mapStyle.setElementType(MapTypeStyleElementType.LABELS); + mapStyle.setElementType(MapTypeStyleElementType.LABELS__TEXT__FILL); MapTypeStyler[] typeStylers = new MapTypeStyler[1]; typeStylers[0] = MapTypeStyler.newInvertLightnessStyler(true); mapStyle.setStylers(typeStylers); - mapTypeStyles[6] = mapStyle; + mapTypeStyles[7] = mapStyle; + + mapStyle = MapTypeStyle.newInstance(); + mapStyle.setFeatureType(MapTypeStyleFeatureType.ADMINISTRATIVE); + mapStyle.setElementType(MapTypeStyleElementType.LABELS__TEXT__STROKE); + typeStylers = new MapTypeStyler[1]; + typeStylers[0] = MapTypeStyler.newInvertLightnessStyler(true); + mapStyle.setStylers(typeStylers); + mapTypeStyles[8] = mapStyle; + + mapStyle = MapTypeStyle.newInstance(); + mapStyle.setFeatureType(MapTypeStyleFeatureType.ADMINISTRATIVE); + mapStyle.setElementType(MapTypeStyleElementType.GEOMETRY__FILL); + typeStylers = new MapTypeStyler[3]; + typeStylers[0] = MapTypeStyler.newHueStyler("#ffffff"); + typeStylers[1] = MapTypeStyler.newLightnessStyler(-70); + typeStylers[2] = MapTypeStyler.newSaturationStyler(-100); + mapStyle.setStylers(typeStylers); + mapTypeStyles[9] = mapStyle; - } else { + mapStyle = MapTypeStyle.newInstance(); + mapStyle.setFeatureType(MapTypeStyleFeatureType.ADMINISTRATIVE); + mapStyle.setElementType(MapTypeStyleElementType.GEOMETRY__STROKE); + typeStylers = new MapTypeStyler[1]; + typeStylers[0] = MapTypeStyler.newLightnessStyler(-70); + mapStyle.setStylers(typeStylers); + mapTypeStyles[10] = mapStyle; + + } else { mapTypeStyles = new MapTypeStyle[4]; @@ -433,6 +463,20 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ add(map, 0, 0); map.setSize("100%", "100%"); + if (parent.particles > 0) { + map.addBoundsChangeHandler(new BoundsChangeMapHandler() { + @Override + public void onEvent(BoundsChangeMapEvent event) { + // improve browser performance by deferred scheduling of redraws + Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { + public void execute() { + windStreamletsCanvasOverlay.onBoundsChanged(); + } + }); + } + }); + } + /*map.addZoomChangeHandler(new ZoomChangeMapHandler() { @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java new file mode 100644 index 00000000000..9196db79c7d --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java @@ -0,0 +1,12 @@ +package com.sap.sailing.gwt.ui.simulator; + +public class Vector { + + public double x; + public double y; + + public double length() { + return Math.sqrt(x*x + y*y); + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index f7bd4ed4904..ed2e6736f3a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -13,7 +13,6 @@ import com.google.gwt.json.client.JSONNumber; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONString; import com.google.gwt.maps.client.base.LatLng; -import com.google.gwt.maps.client.base.Point; import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO; @@ -37,13 +36,18 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti private boolean visible = false; private Timer timer; + com.google.gwt.user.client.Timer loopTimer; private String[] color; private Mercator projection; private RectField field; private int nParticles; private Particle[] particles; - private LatLng pNE; - private LatLng pSW; + private GeoPos pNE; + private GeoPos pSW; + private boolean swarmOffScreen = false; + private int swarmPause = 0; + private GeoPos boundsNE; + private GeoPos boundsSW; private static Logger logger = Logger.getLogger(WindStreamletsCanvasOverlay.class.getName()); @@ -279,6 +283,10 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti } } + public static native void console(String msg) /*-{ + console.log(msg); + }-*/; + public void gwtSwarmTest() { projection = new Mercator(this, map); @@ -294,71 +302,206 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti projection.calibrate(); } - color = new String[256]; - for(int alpha=0; alpha<=255; alpha++) { - color[alpha] = "rgba(255,255,255,"+(((double)alpha)/255.0)+")"; - } + this.updateBounds(); + + color = field.getColors(); Context2d ctxt = canvas.getContext2d(); ctxt.setFillStyle("white"); - particles = new Particle[nParticles]; + particles = this.makeNewParticles(); - pNE = map.getBounds().getNorthEast(); - pSW = map.getBounds().getSouthWest(); + /*// Create animation-loop using scheduler for repeated method calls + Scheduler scheduler = Scheduler.get(); + scheduler.scheduleFixedPeriod(this, 40); + //scheduler.scheduleFixedDelay(this, 10);*/ + + startLoop(40); + } - for(int idx=0; idx 0)||(visibleSW.y < 0)||(visibleNE.x < 0)||(visibleSW.x > 0); + + if (swarmOffScreen) { + + this.boundsNE = fieldNE; + this.boundsSW = fieldSW; + + } else { + + if ((!useBoundsNorth)&&(!useBoundsEast)) { + this.boundsNE = mapNE; + } else if (!useBoundsNorth) { + this.boundsNE = new GeoPos(mapNE.lat, fieldNE.lng); + } else if (!useBoundsEast) { + this.boundsNE = new GeoPos(fieldNE.lat, mapNE.lng); + } else { + this.boundsNE = fieldNE; + } + + if ((!useBoundsSouth)&&(!useBoundsWest)) { + this.boundsSW = mapSW; + } else if (!useBoundsSouth) { + this.boundsSW = new GeoPos(mapSW.lat, fieldSW.lng); + } else if (!useBoundsWest) { + this.boundsSW = new GeoPos(fieldSW.lat, mapSW.lng); + } else { + this.boundsSW = fieldSW; + } } - Scheduler scheduler = Scheduler.get(); - scheduler.scheduleFixedPeriod(this, 40); - } + this.field.visX0 = this.boundsSW.lng; + this.field.visY0 = this.boundsSW.lat; + this.field.visX1 = this.boundsNE.lng; + this.field.visY1 = this.boundsNE.lat; + Vector boundsSWpx = this.projection.latlng2pixel(this.boundsSW); + Vector boundsNEpx = this.projection.latlng2pixel(this.boundsNE); + + double boundsWidthpx = Math.abs(boundsNEpx.x - boundsSWpx.x); + double boundsHeightpx = Math.abs(boundsSWpx.y - boundsNEpx.y); + + this.nParticles = (int)Math.round(Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.numParticleFactor); + //this.numParticles = Math.sqrt(boundsWidthpx*boundsWidthpx + boundsHeightpx*boundsHeightpx) * this.field.numParticleFactor; + //console.log("numParticles: "+this.numParticles + " at " + (boundsWidthpx) +"x" + (boundsHeightpx) + "px (" + (boundsWidthpx * boundsHeightpx) + " pixels)"); + }; + + public Vector isVisible(GeoPos pos) { + + // test for visibility of swarm + Vector proj = this.projection.latlng2pixel(pos); + + Vector result = new Vector(); + result.x = (proj.x < 0 ? -1 : 0) + (proj.x > this.getCanvas().getOffsetWidth() ? 1 : 0); + result.y = (proj.y < 0 ? -1 : 0) + (proj.y > this.getCanvas().getOffsetHeight() ? 1 : 0); + + return result; + } + + public void startLoop(final int millis) { + // Create animation-loop based on timer timeout + loopTimer = new com.google.gwt.user.client.Timer() { + public void run() { + Date time0 = new Date(); + if (swarmPause > 1) { + swarmPause--; + } else if (swarmPause == 1) { + setCanvasSettings(); + projection.calibrate(); + updateBounds(); + particles = makeNewParticles(); + swarmPause = 0; + } + //console("swarmOffScreen:"+swarmOffScreen); + if ((!swarmOffScreen)&&(swarmPause == 0)) { + execute(); + } + Date time1 = new Date(); + //console("delta:"+(time1.getTime()-time0.getTime())+"/"+millis); + loopTimer.schedule((int)Math.max(10, millis - (time1.getTime()-time0.getTime()))); + } + }; + loopTimer.schedule(millis); + } + public void drawSwarm() { + Context2d ctxt = canvas.getContext2d(); - ctxt.setGlobalAlpha(0.05); + ctxt.setGlobalAlpha(0.08); ctxt.setGlobalCompositeOperation("destination-out"); ctxt.setFillStyle("black"); ctxt.fillRect(0, 0, canvas.getOffsetWidth(), canvas.getOffsetHeight()); ctxt.setGlobalAlpha(1.0); ctxt.setGlobalCompositeOperation("source-over"); ctxt.setFillStyle("white"); + //int nDrawn = 0; for(int idx=0; idx= Math.random())&&(v != null)) { particle.age = Math.max(2, (int)Math.round(Math.random()*40)); @@ -373,14 +516,13 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti if (particle.v == null) { particle.age = 0; } else { - double lat = particle.pos.getLatitude() + off*particle.v.getY(); - double lng = particle.pos.getLongitude() + off*particle.v.getX(); - double s = RectField.length(particle.v) / field.getMaxLength(); + particle.pos.lat = particle.pos.lat + speed*particle.v.y; + particle.pos.lng = particle.pos.lng + speed*particle.v.x; + double s = particle.v.length() / field.getMaxLength(); particle.alpha = (int)Math.min(255, 90 + Math.round(350 * s)); - particle.pos = LatLng.newInstance(lat, lng); particle.age--; if (particle.age > 0) { - particle.v = field.getValue(particle.pos); + particle.v = field.getVector(particle.pos); } } } From 1802b0c33f9d449c6279ae3de83a18cf1ba2b887 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Mon, 28 Apr 2014 22:32:07 +0200 Subject: [PATCH 07/66] move swarm animation into class Swarm; --- .../ui/simulator/PathLegendCanvasOverlay.java | 2 +- .../gwt/ui/simulator/SimulatorMap.java | 2 +- .../sap/sailing/gwt/ui/simulator/Swarm.java | 283 ++++++++++++++++++ .../ui/simulator/WindLineCanvasOverlay.java | 2 +- .../WindStreamletsCanvasOverlay.java | 274 +---------------- .../simulator/racemap/FullCanvasOverlay.java | 2 +- 6 files changed, 297 insertions(+), 268 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java index 44fe88a263c..4fe916d694b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java @@ -140,7 +140,7 @@ public class PathLegendCanvasOverlay extends FullCanvasOverlay { } @Override - protected void setCanvasSettings() { + public void setCanvasSettings() { super.setCanvasSettings(); canvas.getElement().getStyle().setZIndex(100000); // make legend float on-top of all overlays } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java index 1a37639f944..dc27c36e75d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java @@ -470,7 +470,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ // improve browser performance by deferred scheduling of redraws Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { public void execute() { - windStreamletsCanvasOverlay.onBoundsChanged(); + windStreamletsCanvasOverlay.getSwarm().onBoundsChanged(); } }); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java new file mode 100644 index 00000000000..2c04c6ad1f5 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java @@ -0,0 +1,283 @@ +package com.sap.sailing.gwt.ui.simulator; + +import java.util.Date; + +import com.google.gwt.canvas.client.Canvas; +import com.google.gwt.canvas.dom.client.Context2d; +import com.google.gwt.core.client.GWT; +import com.google.gwt.maps.client.MapWidget; +import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; + +public class Swarm { + + protected FullCanvasOverlay fullcanvas; + protected Canvas canvas; + protected MapWidget map; + + com.google.gwt.user.client.Timer loopTimer; + private String[] color; + private Mercator projection; + private RectField field; + private int nParticles; + private Particle[] particles; + //private GeoPos pNE; + //private GeoPos pSW; + private boolean swarmOffScreen = false; + private int swarmPause = 0; + private GeoPos boundsNE; + private GeoPos boundsSW; + + public Swarm(FullCanvasOverlay fullcanvas, MapWidget map) { + this.fullcanvas = fullcanvas; + this.canvas = fullcanvas.getCanvas(); + this.map = map; + } + + public void start() { + + projection = new Mercator(fullcanvas, map); + + if (field == null) { + SimulatorJSBundle bundle = GWT.create(SimulatorJSBundle.class); + String jsonStr = bundle.windStreamletsDataJS().getText(); + field = RectField.read(jsonStr.substring(19, jsonStr.length()-1), false); + + map.setZoom(5); + map.panTo(field.getCenter()); + + projection.calibrate(); + } + + this.updateBounds(); + + color = field.getColors(); + + Context2d ctxt = canvas.getContext2d(); + ctxt.setFillStyle("white"); + + particles = this.makeNewParticles(); + + /*// Create animation-loop using scheduler for repeated method calls + Scheduler scheduler = Scheduler.get(); + scheduler.scheduleFixedPeriod(this, 40); + //scheduler.scheduleFixedDelay(this, 10);*/ + + startLoop(40); + } + + public Particle[] makeNewParticles() { + + Particle[] newParticles = new Particle[nParticles]; + + for(int idx=0; idx 0)||(visibleSW.y < 0)||(visibleNE.x < 0)||(visibleSW.x > 0); + + if (swarmOffScreen) { + + this.boundsNE = fieldNE; + this.boundsSW = fieldSW; + + } else { + + if ((!useBoundsNorth)&&(!useBoundsEast)) { + this.boundsNE = mapNE; + } else if (!useBoundsNorth) { + this.boundsNE = new GeoPos(mapNE.lat, fieldNE.lng); + } else if (!useBoundsEast) { + this.boundsNE = new GeoPos(fieldNE.lat, mapNE.lng); + } else { + this.boundsNE = fieldNE; + } + + if ((!useBoundsSouth)&&(!useBoundsWest)) { + this.boundsSW = mapSW; + } else if (!useBoundsSouth) { + this.boundsSW = new GeoPos(mapSW.lat, fieldSW.lng); + } else if (!useBoundsWest) { + this.boundsSW = new GeoPos(fieldSW.lat, mapSW.lng); + } else { + this.boundsSW = fieldSW; + } + + } + + this.field.visX0 = this.boundsSW.lng; + this.field.visY0 = this.boundsSW.lat; + this.field.visX1 = this.boundsNE.lng; + this.field.visY1 = this.boundsNE.lat; + + Vector boundsSWpx = this.projection.latlng2pixel(this.boundsSW); + Vector boundsNEpx = this.projection.latlng2pixel(this.boundsNE); + + double boundsWidthpx = Math.abs(boundsNEpx.x - boundsSWpx.x); + double boundsHeightpx = Math.abs(boundsSWpx.y - boundsNEpx.y); + + this.nParticles = (int)Math.round(Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.numParticleFactor); + //this.numParticles = Math.sqrt(boundsWidthpx*boundsWidthpx + boundsHeightpx*boundsHeightpx) * this.field.numParticleFactor; + //console.log("numParticles: "+this.numParticles + " at " + (boundsWidthpx) +"x" + (boundsHeightpx) + "px (" + (boundsWidthpx * boundsHeightpx) + " pixels)"); + }; + + public Vector isVisible(GeoPos pos) { + + // test for visibility of swarm + Vector proj = this.projection.latlng2pixel(pos); + + Vector result = new Vector(); + result.x = (proj.x < 0 ? -1 : 0) + (proj.x > canvas.getOffsetWidth() ? 1 : 0); + result.y = (proj.y < 0 ? -1 : 0) + (proj.y > canvas.getOffsetHeight() ? 1 : 0); + + return result; + } + + public void startLoop(final int millis) { + // Create animation-loop based on timer timeout + loopTimer = new com.google.gwt.user.client.Timer() { + public void run() { + Date time0 = new Date(); + if (swarmPause > 1) { + swarmPause--; + } else if (swarmPause == 1) { + fullcanvas.setCanvasSettings(); + projection.calibrate(); + updateBounds(); + particles = makeNewParticles(); + swarmPause = 0; + } + //console("swarmOffScreen:"+swarmOffScreen); + if ((!swarmOffScreen)&&(swarmPause == 0)) { + execute(); + } + Date time1 = new Date(); + //console("delta:"+(time1.getTime()-time0.getTime())+"/"+millis); + loopTimer.schedule((int)Math.max(10, millis - (time1.getTime()-time0.getTime()))); + } + }; + loopTimer.schedule(millis); + } + + public void drawSwarm() { + + Context2d ctxt = canvas.getContext2d(); + + ctxt.setGlobalAlpha(0.08); + ctxt.setGlobalCompositeOperation("destination-out"); + ctxt.setFillStyle("black"); + ctxt.fillRect(0, 0, canvas.getOffsetWidth(), canvas.getOffsetHeight()); + ctxt.setGlobalAlpha(1.0); + ctxt.setGlobalCompositeOperation("source-over"); + ctxt.setFillStyle("white"); + //int nDrawn = 0; + for(int idx=0; idx= Math.random())&&(v != null)) { + particle.age = Math.max(2, (int)Math.round(Math.random()*40)); + particle.pxOld = projection.latlng2pixel(particle.pos); + particle.alpha = 0; + particle.v = v; + done = true; + } + } + } + if (particle.age > 0) { + if (particle.v == null) { + particle.age = 0; + } else { + particle.pos.lat = particle.pos.lat + speed*particle.v.y; + particle.pos.lng = particle.pos.lng + speed*particle.v.x; + double s = particle.v.length() / field.getMaxLength(); + particle.alpha = (int)Math.min(255, 90 + Math.round(350 * s)); + particle.age--; + if (particle.age > 0) { + particle.v = field.getVector(particle.pos); + } + } + } + + } + + drawSwarm(); + + return true; + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindLineCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindLineCanvasOverlay.java index 14f8e0ae6f8..571d2749922 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindLineCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindLineCanvasOverlay.java @@ -263,7 +263,7 @@ public class WindLineCanvasOverlay extends FullCanvasOverlay implements TimeList } @Override - protected void setCanvasSettings() { + public void setCanvasSettings() { if (corners != null && corners.length == 4) { final Point corner0 = getPointInDivPixel(corners[0]); final Point corner1 = getPointInDivPixel(corners[1]); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index ed2e6736f3a..bc34c9f1e50 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -4,10 +4,7 @@ import java.util.Date; import java.util.List; import java.util.logging.Logger; -import com.google.gwt.canvas.dom.client.Context2d; -import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptObject; -import com.google.gwt.core.client.Scheduler; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONNumber; import com.google.gwt.json.client.JSONObject; @@ -26,7 +23,7 @@ import com.sap.sse.gwt.client.player.Timer; * @author Nidhi Sawhney(D054070) * */ -public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements TimeListenerWithStoppingCriteria, Scheduler.RepeatingCommand { +public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements TimeListenerWithStoppingCriteria { /** The wind field that is to be displayed in the overlay */ protected WindFieldDTO windFieldDTO; @@ -35,19 +32,9 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti private boolean visible = false; private Timer timer; - - com.google.gwt.user.client.Timer loopTimer; - private String[] color; - private Mercator projection; - private RectField field; + private int nParticles; - private Particle[] particles; - private GeoPos pNE; - private GeoPos pSW; - private boolean swarmOffScreen = false; - private int swarmPause = 0; - private GeoPos boundsNE; - private GeoPos boundsSW; + private Swarm swarm; private static Logger logger = Logger.getLogger(WindStreamletsCanvasOverlay.class.getName()); @@ -274,8 +261,9 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti protected void draw() { super.draw(); if (mapProjection != null) { - if ((nParticles > 0)&&(projection == null)) { - this.gwtSwarmTest(); + if ((nParticles > 0)&&(swarm == null)) { + this.swarm = new Swarm(this,map); + this.swarm.start(); } if (windFieldDTO != null) { // drawing is done by external JavaScript for Streamlets @@ -283,256 +271,14 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti } } + public Swarm getSwarm() { + return this.swarm; + } + public static native void console(String msg) /*-{ console.log(msg); }-*/; - public void gwtSwarmTest() { - - projection = new Mercator(this, map); - - if (field == null) { - SimulatorJSBundle bundle = GWT.create(SimulatorJSBundle.class); - String jsonStr = bundle.windStreamletsDataJS().getText(); - field = RectField.read(jsonStr.substring(19, jsonStr.length()-1), false); - - map.setZoom(5); - map.panTo(field.getCenter()); - - projection.calibrate(); - } - - this.updateBounds(); - - color = field.getColors(); - - Context2d ctxt = canvas.getContext2d(); - ctxt.setFillStyle("white"); - - particles = this.makeNewParticles(); - - /*// Create animation-loop using scheduler for repeated method calls - Scheduler scheduler = Scheduler.get(); - scheduler.scheduleFixedPeriod(this, 40); - //scheduler.scheduleFixedDelay(this, 10);*/ - - startLoop(40); - } - - public Particle[] makeNewParticles() { - - Particle[] newParticles = new Particle[nParticles]; - - for(int idx=0; idx 0)||(visibleSW.y < 0)||(visibleNE.x < 0)||(visibleSW.x > 0); - - if (swarmOffScreen) { - - this.boundsNE = fieldNE; - this.boundsSW = fieldSW; - - } else { - - if ((!useBoundsNorth)&&(!useBoundsEast)) { - this.boundsNE = mapNE; - } else if (!useBoundsNorth) { - this.boundsNE = new GeoPos(mapNE.lat, fieldNE.lng); - } else if (!useBoundsEast) { - this.boundsNE = new GeoPos(fieldNE.lat, mapNE.lng); - } else { - this.boundsNE = fieldNE; - } - - if ((!useBoundsSouth)&&(!useBoundsWest)) { - this.boundsSW = mapSW; - } else if (!useBoundsSouth) { - this.boundsSW = new GeoPos(mapSW.lat, fieldSW.lng); - } else if (!useBoundsWest) { - this.boundsSW = new GeoPos(fieldSW.lat, mapSW.lng); - } else { - this.boundsSW = fieldSW; - } - - } - - this.field.visX0 = this.boundsSW.lng; - this.field.visY0 = this.boundsSW.lat; - this.field.visX1 = this.boundsNE.lng; - this.field.visY1 = this.boundsNE.lat; - - Vector boundsSWpx = this.projection.latlng2pixel(this.boundsSW); - Vector boundsNEpx = this.projection.latlng2pixel(this.boundsNE); - - double boundsWidthpx = Math.abs(boundsNEpx.x - boundsSWpx.x); - double boundsHeightpx = Math.abs(boundsSWpx.y - boundsNEpx.y); - - this.nParticles = (int)Math.round(Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.numParticleFactor); - //this.numParticles = Math.sqrt(boundsWidthpx*boundsWidthpx + boundsHeightpx*boundsHeightpx) * this.field.numParticleFactor; - //console.log("numParticles: "+this.numParticles + " at " + (boundsWidthpx) +"x" + (boundsHeightpx) + "px (" + (boundsWidthpx * boundsHeightpx) + " pixels)"); - }; - - public Vector isVisible(GeoPos pos) { - - // test for visibility of swarm - Vector proj = this.projection.latlng2pixel(pos); - - Vector result = new Vector(); - result.x = (proj.x < 0 ? -1 : 0) + (proj.x > this.getCanvas().getOffsetWidth() ? 1 : 0); - result.y = (proj.y < 0 ? -1 : 0) + (proj.y > this.getCanvas().getOffsetHeight() ? 1 : 0); - - return result; - } - - public void startLoop(final int millis) { - // Create animation-loop based on timer timeout - loopTimer = new com.google.gwt.user.client.Timer() { - public void run() { - Date time0 = new Date(); - if (swarmPause > 1) { - swarmPause--; - } else if (swarmPause == 1) { - setCanvasSettings(); - projection.calibrate(); - updateBounds(); - particles = makeNewParticles(); - swarmPause = 0; - } - //console("swarmOffScreen:"+swarmOffScreen); - if ((!swarmOffScreen)&&(swarmPause == 0)) { - execute(); - } - Date time1 = new Date(); - //console("delta:"+(time1.getTime()-time0.getTime())+"/"+millis); - loopTimer.schedule((int)Math.max(10, millis - (time1.getTime()-time0.getTime()))); - } - }; - loopTimer.schedule(millis); - } - - public void drawSwarm() { - - Context2d ctxt = canvas.getContext2d(); - - ctxt.setGlobalAlpha(0.08); - ctxt.setGlobalCompositeOperation("destination-out"); - ctxt.setFillStyle("black"); - ctxt.fillRect(0, 0, canvas.getOffsetWidth(), canvas.getOffsetHeight()); - ctxt.setGlobalAlpha(1.0); - ctxt.setGlobalCompositeOperation("source-over"); - ctxt.setFillStyle("white"); - //int nDrawn = 0; - for(int idx=0; idx= Math.random())&&(v != null)) { - particle.age = Math.max(2, (int)Math.round(Math.random()*40)); - particle.pxOld = projection.latlng2pixel(particle.pos); - particle.alpha = 0; - particle.v = v; - done = true; - } - } - } - if (particle.age > 0) { - if (particle.v == null) { - particle.age = 0; - } else { - particle.pos.lat = particle.pos.lat + speed*particle.v.y; - particle.pos.lng = particle.pos.lng + speed*particle.v.x; - double s = particle.v.length() / field.getMaxLength(); - particle.alpha = (int)Math.min(255, 90 + Math.round(350 * s)); - particle.age--; - if (particle.age > 0) { - particle.v = field.getVector(particle.pos); - } - } - } - - } - - drawSwarm(); - - return true; - } private void clear() { this.stopStreamlets(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/racemap/FullCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/racemap/FullCanvasOverlay.java index c63d54e2fcd..e1ff06a09ef 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/racemap/FullCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/racemap/FullCanvasOverlay.java @@ -59,7 +59,7 @@ public abstract class FullCanvasOverlay extends CanvasOverlayV3 implements Requi /** * Set the canvas to be the size of the map and set it to the top left corner of the map */ - protected void setCanvasSettings() { + public void setCanvasSettings() { int canvasWidth = getMap().getDiv().getClientWidth(); int canvasHeight = getMap().getDiv().getClientHeight(); From fe41036b752e742d744a8ebc99992e51047c2d54 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 29 Apr 2014 11:44:19 +0200 Subject: [PATCH 08/66] align GWT particle creation closer to original native JS; some cleanup; --- .../js/wind-streamlets.js | 2 +- .../sailing/gwt/ui/simulator/RectField.java | 8 +- .../sap/sailing/gwt/ui/simulator/Swarm.java | 142 ++++++++---------- .../WindStreamletsCanvasOverlay.java | 7 +- 4 files changed, 67 insertions(+), 92 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/js/wind-streamlets.js b/java/com.sap.sailing.gwt.ui/js/wind-streamlets.js index cdec8a2790e..901f68b5b09 100644 --- a/java/com.sap.sailing.gwt.ui/js/wind-streamlets.js +++ b/java/com.sap.sailing.gwt.ui/js/wind-streamlets.js @@ -636,7 +636,7 @@ Swarm.prototype.updateBounds = function() { var boundsWidthpx = Math.abs(boundsNEpx.x - boundsSWpx.x); var boundsHeightpx = Math.abs(boundsSWpx.y - boundsNEpx.y); - this.numParticles = Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.numParticleFactor; + this.numParticles = Math.round(Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.numParticleFactor); //this.numParticles = Math.sqrt(boundsWidthpx*boundsWidthpx + boundsHeightpx*boundsHeightpx) * this.field.numParticleFactor; if (swarmDebug) { console.log("numParticles: "+this.numParticles + " at " + (boundsWidthpx) +"x" + (boundsHeightpx) + "px (" + (boundsWidthpx * boundsHeightpx) + " pixels)"); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java index 6048b912abb..85bed4e0b84 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java @@ -22,7 +22,7 @@ public class RectField { private int h; private double maxLength; - public double numParticleFactor; + public double particleFactor; public RectField(Vector[][] field, double x0, double y0, double x1, double y1) { this.x0 = x0; @@ -39,7 +39,7 @@ public class RectField { this.w = field.length; this.h = field[0].length; this.maxLength = 0; - this.numParticleFactor = 4.5; + this.particleFactor = 4.5; double mx = 0; double my = 0; @@ -56,10 +56,6 @@ public class RectField { my = (my / this.h) * (y1 - y0) + y0; } - //public static double length(Vector p) { - // return Math.sqrt(p.x*p.x + p.y*p.y); - //} - public static RectField read(String jsonData, boolean correctForSphere) { JSONObject data = JSONParser.parseLenient(jsonData).isObject(); int w = (int)data.get("gridWidth").isNumber().doubleValue(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java index 2c04c6ad1f5..922c9eb46f4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java @@ -6,6 +6,7 @@ import com.google.gwt.canvas.client.Canvas; import com.google.gwt.canvas.dom.client.Context2d; import com.google.gwt.core.client.GWT; import com.google.gwt.maps.client.MapWidget; +import com.google.gwt.user.client.Timer; import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; public class Swarm { @@ -14,16 +15,15 @@ public class Swarm { protected Canvas canvas; protected MapWidget map; - com.google.gwt.user.client.Timer loopTimer; + private Timer loopTimer; private String[] color; private Mercator projection; private RectField field; private int nParticles; private Particle[] particles; - //private GeoPos pNE; - //private GeoPos pSW; private boolean swarmOffScreen = false; private int swarmPause = 0; + private boolean swarmContinue = true; private GeoPos boundsNE; private GeoPos boundsSW; @@ -33,7 +33,7 @@ public class Swarm { this.map = map; } - public void start() { + public void start(int animationDuration) { projection = new Mercator(fullcanvas, map); @@ -55,31 +55,45 @@ public class Swarm { Context2d ctxt = canvas.getContext2d(); ctxt.setFillStyle("white"); - particles = this.makeNewParticles(); + particles = this.createParticles(); - /*// Create animation-loop using scheduler for repeated method calls - Scheduler scheduler = Scheduler.get(); - scheduler.scheduleFixedPeriod(this, 40); - //scheduler.scheduleFixedDelay(this, 10);*/ - - startLoop(40); + this.swarmContinue = true; + startLoop(animationDuration); + } + + public void stop() { + this.swarmContinue = false; } - public Particle[] makeNewParticles() { - - Particle[] newParticles = new Particle[nParticles]; - - for(int idx=0; idx= Math.random()) { + if (v.length() == 0) { + particle.age = 0; + } else { + particle.age = 1 + (int)Math.round(Math.random()*40); + } + particle.pxOld = projection.latlng2pixel(particle.pos); + particle.alpha = 0; + particle.v = v; + done = true; + } } - + return particle; + } + + public Particle[] createParticles() { + + Particle[] newParticles = new Particle[nParticles]; + for(int idx=0; idx= Math.random())&&(v != null)) { - particle.age = Math.max(2, (int)Math.round(Math.random()*40)); - particle.pxOld = projection.latlng2pixel(particle.pos); - particle.alpha = 0; - particle.v = v; - done = true; - } + if ((particle.age > 0) && (this.field.inBounds(particle.pos.lng, particle.pos.lat))) { + particle.pos.lat = particle.pos.lat + speed*particle.v.y; + particle.pos.lng = particle.pos.lng + speed*particle.v.x; + double s = particle.v.length() / field.getMaxLength(); + particle.alpha = (int)Math.min(255, 90 + Math.round(350 * s)); + particle.age--; + if (particle.age > 0) { + particle.v = field.getVector(particle.pos); } + } else { + particles[idx] = this.createParticle(); } - if (particle.age > 0) { - if (particle.v == null) { - particle.age = 0; - } else { - particle.pos.lat = particle.pos.lat + speed*particle.v.y; - particle.pos.lng = particle.pos.lng + speed*particle.v.x; - double s = particle.v.length() / field.getMaxLength(); - particle.alpha = (int)Math.min(255, 90 + Math.round(350 * s)); - particle.age--; - if (particle.age > 0) { - particle.v = field.getVector(particle.pos); - } - } - } - } drawSwarm(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index bc34c9f1e50..c75cd981e5c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -263,7 +263,7 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti if (mapProjection != null) { if ((nParticles > 0)&&(swarm == null)) { this.swarm = new Swarm(this,map); - this.swarm.start(); + this.swarm.start(40); } if (windFieldDTO != null) { // drawing is done by external JavaScript for Streamlets @@ -275,11 +275,6 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti return this.swarm; } - public static native void console(String msg) /*-{ - console.log(msg); - }-*/; - - private void clear() { this.stopStreamlets(); } From 15af984a940b1bc942e9c91c9a40034bd5b71d56 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 29 Apr 2014 11:52:07 +0200 Subject: [PATCH 09/66] extend Vector with setLength(); --- .../java/com/sap/sailing/gwt/ui/simulator/RectField.java | 4 ++-- .../java/com/sap/sailing/gwt/ui/simulator/Vector.java | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java index 85bed4e0b84..579fdf9c115 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java @@ -72,9 +72,9 @@ public class RectField { double uy = y / (h - 1); double lat = data.get("y0").isNumber().doubleValue() * (1 - uy) + data.get("y1").isNumber().doubleValue() * uy; double m = Math.PI * lat / 180; - //double length = length(v); + double length = v.length(); v.x = v.x / Math.cos(m); - //v.setLength(length); + v.setLength(length); } field[x][y] = v; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java index 9196db79c7d..945c242a67d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java @@ -9,4 +9,13 @@ public class Vector { return Math.sqrt(x*x + y*y); } + public Vector setLength(double length) { + double current = this.length(); + if (current > 0) { + double scale = length / current; + this.x *= scale; + this.y *= scale; + } + return this; + } } From c83196f73ddb7d63256edc0a56fa94dd1881fe23 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Sat, 3 May 2014 00:20:45 +0200 Subject: [PATCH 10/66] GWT streamlets in simulator; --- .../sap/sailing/gwt/ui/simulator/Index.java | 12 + .../sailing/gwt/ui/simulator/Neighbors.java | 20 ++ .../sailing/gwt/ui/simulator/RectField.java | 76 +++-- .../gwt/ui/simulator/SimulatorField.java | 297 +++++++++++++++++ .../gwt/ui/simulator/SimulatorMap.java | 6 +- .../sap/sailing/gwt/ui/simulator/Swarm.java | 49 ++- .../sap/sailing/gwt/ui/simulator/Vector.java | 8 + .../sailing/gwt/ui/simulator/VectorField.java | 40 +++ .../WindStreamletsCanvasOverlay.java | 64 ++-- .../WindStreamletsCanvasOverlayJS.java | 304 ++++++++++++++++++ 10 files changed, 806 insertions(+), 70 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Index.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Neighbors.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorField.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/VectorField.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlayJS.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Index.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Index.java new file mode 100644 index 00000000000..05fd28816b7 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Index.java @@ -0,0 +1,12 @@ +package com.sap.sailing.gwt.ui.simulator; + +public class Index { + + public long x; + public long y; + + public Index(long x, long y) { + this.x = x; + this.y = y; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Neighbors.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Neighbors.java new file mode 100644 index 00000000000..2ffbb5ef913 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Neighbors.java @@ -0,0 +1,20 @@ +package com.sap.sailing.gwt.ui.simulator; + +public class Neighbors { + + public int xBot; + public int xTop; + public double xMod; + public int yBot; + public int yTop; + public double yMod; + + public Neighbors(int xTop, int yTop, int xBot, int yBot, double xMod, double yMod) { + this.xTop = xTop; + this.yTop = yTop; + this.xBot = xBot; + this.yBot = yBot; + this.xMod = xMod; + this.yMod = yMod; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java index 579fdf9c115..6613cfab278 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java @@ -4,19 +4,22 @@ import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONParser; import com.google.gwt.maps.client.base.LatLng; -public class RectField { +public class RectField implements VectorField { - Vector[][] field; + private Vector[][] field; public double x0; public double x1; public double y0; public double y1; - public double visX0; + //public double visX0; public double visX1; public double visY0; public double visY1; + + public GeoPos visSW; + public GeoPos visNE; private int w; private int h; @@ -30,11 +33,9 @@ public class RectField { this.y0 = y0; this.y1 = y1; - this.visX0 = 0; - this.visY0 = 0; - this.visX1 = 0; - this.visY1 = 0; - + this.visSW = new GeoPos(0.0, 0.0); + this.visNE = new GeoPos(0.0, 0.0); + this.field = field; this.w = field.length; this.h = field[0].length; @@ -83,26 +84,35 @@ public class RectField { return result; } + public void setStep(int step) { + } + + public void nextStep() { + } + + public void prevStep() { + } + public GeoPos getRandomPosition() { double rndY = Math.random(); double rndX = Math.random(); GeoPos result = new GeoPos(); - result.lat = rndY * this.visY0 + (1 - rndY) * this.visY1; - result.lng = rndX * this.visX0 + (1 - rndX) * this.visX1; + result.lat = rndY * this.visSW.lat + (1 - rndY) * this.visNE.lat; + result.lng = rndX * this.visSW.lng + (1 - rndX) * this.visNE.lng; return result; } - public boolean inBounds(double x, double y) { - return x >= this.x0 && x < this.x1 && y >= this.y0 && y < this.y1; + public boolean inBounds(GeoPos p) { + return p.lng >= this.x0 && p.lng < this.x1 && p.lat >= this.y0 && p.lat < this.y1; } - public Vector interpolate(double a, double b) { - int na = (int)Math.floor(a); - int nb = (int)Math.floor(b); - int ma = (int)Math.ceil(a); - int mb = (int)Math.ceil(b); - double fa = a - na; - double fb = b - nb; + public Vector interpolate(GeoPos p) { + int na = (int)Math.floor(p.lng); + int nb = (int)Math.floor(p.lat); + int ma = (int)Math.ceil(p.lng); + int mb = (int)Math.ceil(p.lat); + double fa = p.lng - na; + double fb = p.lat - nb; Vector result = new Vector(); result.x = this.field[na][nb].x * (1 - fa) * (1 - fb) + this.field[ma][nb].x * fa * (1 - fb) + this.field[na][mb].x * (1 - fa) * fb + this.field[ma][mb].x * fa * fb; @@ -113,12 +123,13 @@ public class RectField { public Vector getVector(GeoPos p) { - double a = (this.w - 1 - 1e-6) * (p.lng - this.x0) / (this.x1 - this.x0); - double b = (this.h - 1 - 1e-6) * (p.lat - this.y0) / (this.y1 - this.y0); - if ((a < 0)||(a > (this.w-1))||(b < 0)||(b > (this.h-1))) { + GeoPos q = new GeoPos(); + q.lng = (this.w - 1 - 1e-6) * (p.lng - this.x0) / (this.x1 - this.x0); + q.lat = (this.h - 1 - 1e-6) * (p.lat - this.y0) / (this.y1 - this.y0); + if ((q.lng < 0)||(q.lng > (this.w-1))||(q.lat < 0)||(q.lat > (this.h-1))) { return null; } else { - return this.interpolate(a, b); + return this.interpolate(q); } } @@ -153,4 +164,23 @@ public class RectField { return 1.0; } + public GeoPos getFieldNE() { + return new GeoPos(Math.max(this.y0, this.y1), Math.max(this.x0, this.x1)); + } + + public GeoPos getFieldSW() { + return new GeoPos(Math.min(this.y0, this.y1), Math.min(this.x0, this.x1)); + } + + public void setVisNE(GeoPos visNE) { + this.visNE = visNE; + } + + public void setVisSW(GeoPos visSW) { + this.visSW = visSW; + } + + public double getParticleFactor() { + return this.particleFactor; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorField.java new file mode 100644 index 00000000000..5ef1385938d --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorField.java @@ -0,0 +1,297 @@ +package com.sap.sailing.gwt.ui.simulator; + +import java.util.List; + +import com.google.gwt.json.client.JSONObject; +import com.google.gwt.json.client.JSONParser; +import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; +import com.sap.sailing.gwt.ui.shared.WindFieldDTO; +import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO; + +public class SimulatorField implements VectorField { + + private boolean swarmDebug = false; + + private GeoPos rcStart; + private GeoPos rcEnd; + + private int resY; + private int resX; + + private int borderY; + private int borderX; + + private double bdXi; + private double bdPhi; + + private GeoPos bdA; + private GeoPos bdB; + private GeoPos bdC; + + private double xScale; + + private double x0; + private double x1; + private double y0; + private double y1; + + public GeoPos visSW; + public GeoPos visNE; + + private double maxLength; + private double particleFactor; + + private double lngScale; + + private GeoPos nvY; + private GeoPos nvX; + private GeoPos gvX; + + private double[][][] data; + private int step; + + public SimulatorField(WindFieldDTO windData, WindFieldGenParamsDTO windParams) { + + this.step = 0; + + String parseString = windData.windDataJSON.substring(18, windData.windDataJSON.length()-1) + "}"; + JSONObject baseData = JSONParser.parseLenient(parseString).isObject(); + + this.rcStart = new GeoPos(); + this.rcStart.lat = baseData.get("rcStart").isObject().get("lat").isNumber().doubleValue(); + this.rcStart.lng = baseData.get("rcStart").isObject().get("lng").isNumber().doubleValue(); + + this.rcEnd = new GeoPos(); + this.rcEnd.lat = baseData.get("rcEnd").isObject().get("lat").isNumber().doubleValue(); + this.rcEnd.lng = baseData.get("rcEnd").isObject().get("lng").isNumber().doubleValue(); + + this.resY = (int)baseData.get("resY").isNumber().doubleValue(); + this.resX = (int)baseData.get("resX").isNumber().doubleValue(); + + this.borderY = (int)baseData.get("borderY").isNumber().doubleValue(); + this.borderX = (int)baseData.get("borderX").isNumber().doubleValue(); + + this.bdXi = (this.borderY + 0.5) / (this.resY - 1); + this.bdPhi = 1.0 + 2*this.bdXi; + this.bdA = new GeoPos(this.rcEnd.lat+(this.rcEnd.lat-this.rcStart.lat)*this.bdXi, this.rcEnd.lng+(this.rcEnd.lng-this.rcStart.lng)*this.bdXi); + this.bdB = new GeoPos((this.rcStart.lat-this.rcEnd.lat)*this.bdPhi, (this.rcStart.lng-this.rcEnd.lng)*this.bdPhi); + + this.xScale = baseData.get("xScale").isNumber().doubleValue(); + + this.x0 = baseData.get("boundsSW").isObject().get("lng").isNumber().doubleValue(); + this.x1 = baseData.get("boundsNE").isObject().get("lng").isNumber().doubleValue(); + this.y0 = baseData.get("boundsSW").isObject().get("lat").isNumber().doubleValue(); + this.y1 = baseData.get("boundsNE").isObject().get("lat").isNumber().doubleValue(); + + this.visSW = new GeoPos(0.0, 0.0); + this.visNE = new GeoPos(0.0, 0.0); + + List gridData = windData.getMatrix(); + + int p = 0; + int imax = windParams.getyRes() + 2*windParams.getBorderY(); + int jmax = windParams.getxRes() + 2*windParams.getBorderX(); + int steps = gridData.size() / (imax * jmax); + + this.data = new double[steps][imax][2*jmax]; + + double maxWindSpeed = 0; + for(int s=0; s maxWindSpeed) { + maxWindSpeed = wind.trueWindSpeedInKnots; + } + + this.data[s][i][2*j+1] = wind.trueWindSpeedInKnots*Math.cos(wind.trueWindBearingDeg*Math.PI/180.0); + this.data[s][i][2*j] = wind.trueWindSpeedInKnots*Math.sin(wind.trueWindBearingDeg*Math.PI/180.0); + + } + } + } + this.maxLength = maxWindSpeed; + + this.particleFactor = 2.0; + + double latAvg = (this.rcEnd.lat + this.rcStart.lat) / 2.; + this.lngScale = Math.cos(latAvg * Math.PI / 180.0); + + double difLat = this.rcEnd.lat - this.rcStart.lat; + double difLng = (this.rcEnd.lng - this.rcStart.lng) * this.lngScale; + double difLen = Math.sqrt(difLat*difLat + difLng*difLng); + this.nvY = new GeoPos(difLat/difLen/difLen*(this.resY-1), difLng/difLen/difLen*(this.resY-1)); + + double nrmLat = -difLng/difLen; + double nrmLng = difLat/difLen; + this.nvX = new GeoPos( nrmLat/this.xScale/difLen*(this.resX-1), nrmLng/this.xScale/difLen*(this.resX-1) ); + this.gvX = new GeoPos( nrmLat*this.xScale*difLen, nrmLng/this.lngScale*this.xScale*difLen ); + this.bdC = new GeoPos( this.gvX.lat*(this.resX+2*this.borderX-1)/(this.resX-1), this.gvX.lng*(this.resX+2*this.borderX-1)/(this.resX-1) ); + } + + public static native void console(String msg) /*-{ + console.log(msg); + }-*/; + + public GeoPos getRandomPosition() { + double rndY = Math.random(); + double rndX = Math.random() - 0.5; + GeoPos result = new GeoPos(); + result.lat = this.bdA.lat + rndY * this.bdB.lat + rndX * this.bdC.lat; + result.lng = this.bdA.lng + rndY * this.bdB.lng + rndX * this.bdC.lng; + + if (swarmDebug&&(!this.inBounds(result))) { + console("random-position: out of bounds"); + } + + return result; + } + + + public boolean inBounds(GeoPos p) { + Index idx = this.getIndex(p); + boolean inBool = (idx.x >= 0) && (idx.x < (this.resX+2*this.borderX)) && (idx.y >= 0) && (idx.y < (this.resY+2*this.borderY)); + return inBool; + } + + + public Vector interpolate(GeoPos p) { + + Neighbors idx = this.getNeighbors(p); + + if (swarmDebug&&((idx.xTop >= (this.resX+2*this.borderX))||(idx.yTop >= (this.resY+2*this.borderY)))) { + console("interpolate: out of range: " + idx.xTop + " " + idx.yTop); + } + + //System.out.println("neighbors:"+idx.xBot+","+idx.xTop+","+idx.yBot+","+idx.yTop); + + double avgX = this.data[this.step][idx.yBot][2*idx.xBot] * (1 - idx.yMod) * (1 - idx.xMod) + this.data[this.step][idx.yTop][2*idx.xBot] * idx.yMod * (1 - idx.xMod) + + this.data[this.step][idx.yBot][2*idx.xTop] * (1 - idx.yMod) * idx.xMod + this.data[this.step][idx.yTop][2*idx.xTop] * idx.yMod * idx.xMod; + double avgY = this.data[this.step][idx.yBot][2*idx.xBot+1] * (1 - idx.yMod) * (1 - idx.xMod) + this.data[this.step][idx.yTop][2*idx.xBot+1] * idx.yMod * (1 - idx.xMod) + + this.data[this.step][idx.yBot][2*idx.xTop+1] * (1 - idx.yMod) * idx.xMod + this.data[this.step][idx.yTop][2*idx.xTop+1] * idx.yMod * idx.xMod; + + return new Vector(avgX / this.lngScale, avgY); + } + + public void setStep(int step) { + if (step < 0) { + this.step = 0; + } else if (step >= this.data.length) { + this.step = this.data.length-1; + } else { + this.step = step; + } + } + + public void nextStep() { + if (this.step < (this.data.length-1)) { + this.step++; + } + } + + public void prevStep() { + if (this.step > 0) { + this.step--; + } + } + + public Vector getVector(GeoPos p) { + return this.interpolate(p); + } + + public Index getIndex(GeoPos p) { + + // calculate grid indexes + GeoPos posR = new GeoPos( p.lat - this.rcStart.lat, (p.lng - this.rcStart.lng) * this.lngScale ); + + // closest grid point + long yIdx = Math.round( posR.lat * this.nvY.lat + posR.lng * this.nvY.lng ) + this.borderY; + long xIdx = Math.round( posR.lat * this.nvX.lat + posR.lng * this.nvX.lng + (this.resX - 1) / 2. ) + this.borderX; + + return new Index( xIdx, yIdx ); + } + + public Neighbors getNeighbors(GeoPos p) { + + // calculate grid indexes + GeoPos posR = new GeoPos( p.lat - this.rcStart.lat, (p.lng - this.rcStart.lng) * this.lngScale ); + + // surrounding grid points + double yFlt = posR.lat * this.nvY.lat + posR.lng * this.nvY.lng + this.borderY; + double xFlt = posR.lat * this.nvX.lat + posR.lng * this.nvX.lng + (this.resX - 1) / 2. + this.borderX; + double yBot = Math.floor( yFlt ); + double xBot = Math.floor( xFlt ); + double yTop = Math.ceil( yFlt ); + double xTop = Math.ceil( xFlt ); + double yMod = yFlt - yBot; + double xMod = xFlt - xBot; + + if (xBot < 0) { + xBot = 0; + } + + if (yBot < 0) { + yBot = 0; + } + + if (xTop >= (this.resX+2*this.borderX)) { + xTop = this.resX+2*this.borderX-1; + } + + if (yTop >= (this.resY+2*this.borderY)) { + yTop = this.resY+2*this.borderY-1; + } + + //System.out.println("neighbors:"+xFlt+","+xBot+","+xTop+","+yFlt+","+yBot+","+yTop); + + return new Neighbors((int)xTop, (int)yTop, (int)xBot, (int)yBot, xMod, yMod ); + } + + public double getMaxLength() { + return maxLength; + } + + public double motionScale(int zoomLevel) { + return 0.08 * Math.pow(1.6, Math.min(1.0, 6.0 - zoomLevel)); + } + + public double particleWeight(GeoPos p, Vector v) { + return v.length() / this.maxLength + 0.1; + } + + public String[] getColors() { + String[] colors = new String[256]; + double alpha = 0.7; + int greyValue = 255; + for (int i = 0; i < 256; i++) { + colors[i] = "rgba(" + (greyValue) + "," + (greyValue) + "," + (greyValue) + "," + (alpha*i/255.0) + ")"; + //this.colors[i] = 'hsla(' + 360*(0.55+0.9*(0.5-i/255)) + ',' + (100) + '% ,' + (50) + '%,' + (i/255) + ')'; + } + return colors; + } + + public double lineWidth(int alpha) { + return 1.0; + } + + public GeoPos getFieldNE() { + return new GeoPos(Math.max(this.y0, this.y1), Math.max(this.x0, this.x1)); + } + + public GeoPos getFieldSW() { + return new GeoPos(Math.min(this.y0, this.y1), Math.min(this.x0, this.x1)); + } + + public void setVisNE(GeoPos visNE) { + this.visNE = visNE; + } + + public void setVisSW(GeoPos visSW) { + this.visSW = visSW; + } + + public double getParticleFactor() { + return this.particleFactor; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java index dc27c36e75d..980d555e9fb 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java @@ -463,14 +463,16 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ add(map, 0, 0); map.setSize("100%", "100%"); - if (parent.particles > 0) { + if (windParams.isShowStreamlets2()) { map.addBoundsChangeHandler(new BoundsChangeMapHandler() { @Override public void onEvent(BoundsChangeMapEvent event) { // improve browser performance by deferred scheduling of redraws Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { public void execute() { - windStreamletsCanvasOverlay.getSwarm().onBoundsChanged(); + if (windStreamletsCanvasOverlay.getSwarm() != null) { + windStreamletsCanvasOverlay.getSwarm().onBoundsChanged(); + } } }); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java index 922c9eb46f4..1b7609e7594 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java @@ -7,6 +7,7 @@ import com.google.gwt.canvas.dom.client.Context2d; import com.google.gwt.core.client.GWT; import com.google.gwt.maps.client.MapWidget; import com.google.gwt.user.client.Timer; +import com.sap.sailing.gwt.ui.shared.WindFieldDTO; import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; public class Swarm { @@ -18,7 +19,7 @@ public class Swarm { private Timer loopTimer; private String[] color; private Mercator projection; - private RectField field; + private VectorField field; private int nParticles; private Particle[] particles; private boolean swarmOffScreen = false; @@ -33,19 +34,27 @@ public class Swarm { this.map = map; } - public void start(int animationDuration) { + public void start(int animationDuration, WindFieldDTO windField) { projection = new Mercator(fullcanvas, map); - if (field == null) { + if (windField == null) { + SimulatorJSBundle bundle = GWT.create(SimulatorJSBundle.class); String jsonStr = bundle.windStreamletsDataJS().getText(); field = RectField.read(jsonStr.substring(19, jsonStr.length()-1), false); map.setZoom(5); - map.panTo(field.getCenter()); + map.panTo(((RectField)field).getCenter()); projection.calibrate(); + + } else { + + field = new SimulatorField(((WindStreamletsCanvasOverlay)fullcanvas).windFieldDTO, ((WindStreamletsCanvasOverlay)fullcanvas).windParams); + fullcanvas.setCanvasSettings(); + projection.calibrate(); + } this.updateBounds(); @@ -53,7 +62,13 @@ public class Swarm { color = field.getColors(); Context2d ctxt = canvas.getContext2d(); - ctxt.setFillStyle("white"); + ctxt.setFillStyle("red"); + + /*Vector p = this.projection.latlng2pixel(this.field.getFieldNE()); + ctxt.fillRect(p.x,p.y,5.0,5.0); + + p = this.projection.latlng2pixel(this.field.getFieldSW()); + ctxt.fillRect(p.x,p.y,5.0,5.0);*/ particles = this.createParticles(); @@ -115,8 +130,8 @@ public class Swarm { GeoPos mapNE = new GeoPos(map.getBounds().getNorthEast()); GeoPos mapSW = new GeoPos(map.getBounds().getSouthWest()); - GeoPos fieldNE = new GeoPos(Math.max(this.field.y0, this.field.y1), Math.max(this.field.x0, this.field.x1)); - GeoPos fieldSW = new GeoPos(Math.min(this.field.y0, this.field.y1), Math.min(this.field.x0, this.field.x1)); + GeoPos fieldNE = this.field.getFieldNE(); + GeoPos fieldSW = this.field.getFieldSW(); Vector visibleNE = this.isVisible(fieldNE); Vector visibleSW = this.isVisible(fieldSW); @@ -157,10 +172,8 @@ public class Swarm { } - this.field.visX0 = this.boundsSW.lng; - this.field.visY0 = this.boundsSW.lat; - this.field.visX1 = this.boundsNE.lng; - this.field.visY1 = this.boundsNE.lat; + this.field.setVisSW(this.boundsSW); + this.field.setVisNE(this.boundsNE); Vector boundsSWpx = this.projection.latlng2pixel(this.boundsSW); Vector boundsNEpx = this.projection.latlng2pixel(this.boundsNE); @@ -168,7 +181,7 @@ public class Swarm { double boundsWidthpx = Math.abs(boundsNEpx.x - boundsSWpx.x); double boundsHeightpx = Math.abs(boundsSWpx.y - boundsNEpx.y); - this.nParticles = (int)Math.round(Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.particleFactor); + this.nParticles = (int)Math.round(Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.getParticleFactor()); //console("#particles: "+this.nParticles + " at " + (boundsWidthpx) +"x" + (boundsHeightpx) + "px (" + (boundsWidthpx * boundsHeightpx) + " pixels)"); }; @@ -245,14 +258,16 @@ public class Swarm { for(int idx=0; idx 0) && (this.field.inBounds(particle.pos.lng, particle.pos.lat))) { + if ((particle.age > 0) && (particle.v != null)) { particle.pos.lat = particle.pos.lat + speed*particle.v.y; particle.pos.lng = particle.pos.lng + speed*particle.v.x; double s = particle.v.length() / field.getMaxLength(); particle.alpha = (int)Math.min(255, 90 + Math.round(350 * s)); particle.age--; - if (particle.age > 0) { + if ((particle.age > 0) && (this.field.inBounds(particle.pos))) { particle.v = field.getVector(particle.pos); + } else { + particle.v = null; } } else { particles[idx] = this.createParticle(); @@ -263,5 +278,9 @@ public class Swarm { return true; } - + + public VectorField getField() { + return field; + } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java index 945c242a67d..42118aa0cc1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java @@ -5,6 +5,14 @@ public class Vector { public double x; public double y; + public Vector() { + } + + public Vector(double x, double y) { + this.x = x; + this.y = y; + } + public double length() { return Math.sqrt(x*x + y*y); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/VectorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/VectorField.java new file mode 100644 index 00000000000..9236f3ca594 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/VectorField.java @@ -0,0 +1,40 @@ +package com.sap.sailing.gwt.ui.simulator; + + +public interface VectorField { + + public GeoPos getRandomPosition(); + + public boolean inBounds(GeoPos p); + + public Vector interpolate(GeoPos p); + + public Vector getVector(GeoPos p); + + public double getMaxLength(); + + public double motionScale(int zoomLevel); + + public double particleWeight(GeoPos p, Vector v); + + public String[] getColors(); + + public double lineWidth(int alpha); + + public GeoPos getFieldNE(); + + public GeoPos getFieldSW(); + + public void setVisNE(GeoPos visNE); + + public void setVisSW(GeoPos visSW); + + public double getParticleFactor(); + + public void setStep(int step); + + public void nextStep(); + + public void prevStep(); + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index c75cd981e5c..96af2f85ba2 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -52,11 +52,6 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti getCanvas().getElement().setId("swarm-display"); - /*if (!this.isSwarmDataExt()) { - // random test windfield - JavaScriptObject swarmData = this.getJSONRandomWindData(); - this.setSwarmData(swarmData); - }*/ } public JavaScriptObject getJSONRandomWindData() { @@ -143,24 +138,41 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti $wnd.swarmCanvasProjection = instance; }-*/; - public native void startStreamlets() /*-{ - if ($wnd.swarmAnimator) { - $wnd.swarmUpdData = true; - $wnd.updateStreamlets($wnd.swarmUpdData); - } else { - $wnd.initStreamlets($wnd.swarmMap); +// public native void startStreamlets() /*-{ +// if ($wnd.swarmAnimator) { +// $wnd.swarmUpdData = true; +// $wnd.updateStreamlets($wnd.swarmUpdData); +// } else { +// $wnd.initStreamlets($wnd.swarmMap); +// } +// }-*/; + + public void startStreamlets() { + if (swarm == null) { + this.swarm = new Swarm(this,map); } - }-*/; + this.swarm.start(40, windFieldDTO); + } + +// public native void setStreamletsStep(int step) /*-{ +// $wnd.swarmField.setStep(step); +// }-*/; - public native void setStreamletsStep(int step) /*-{ - $wnd.swarmField.setStep(step); - }-*/; + public void setStreamletsStep(int step) { + this.swarm.getField().setStep(step); + } - public native void stopStreamlets() /*-{ - if ($wnd.stopStreamlets) { - $wnd.stopStreamlets(); - }; - }-*/; +// public native void stopStreamlets() /*-{ +// if ($wnd.stopStreamlets) { +// $wnd.stopStreamlets(); +// }; +// }-*/; + + public void stopStreamlets() { + if (swarm != null) { + this.swarm.stop(); + } + } public void extendWindDataJSON() { @@ -207,14 +219,6 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti public void setWindField(final WindFieldDTO windFieldDTO) { this.windFieldDTO = windFieldDTO; - - this.extendWindDataJSON(); - this.setWindDataJSON(this.windFieldDTO.windDataJSON); - - //this.getJSNIWind(this); - - this.setMapInstance(this.simulatorMap.getMap().getJso()); - this.setCanvasProjectionInstance(this.simulatorMap.getRegattaAreaCanvasOverlay().getMapProjection()); } @Override @@ -263,7 +267,7 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti if (mapProjection != null) { if ((nParticles > 0)&&(swarm == null)) { this.swarm = new Swarm(this,map); - this.swarm.start(40); + this.swarm.start(40, null); } if (windFieldDTO != null) { // drawing is done by external JavaScript for Streamlets @@ -276,7 +280,7 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti } private void clear() { - this.stopStreamlets(); + this.stopStreamlets(); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlayJS.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlayJS.java new file mode 100644 index 00000000000..6ad8d586eee --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlayJS.java @@ -0,0 +1,304 @@ +package com.sap.sailing.gwt.ui.simulator; + +import java.util.Date; +import java.util.List; +import java.util.logging.Logger; + +import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.json.client.JSONArray; +import com.google.gwt.json.client.JSONNumber; +import com.google.gwt.json.client.JSONObject; +import com.google.gwt.json.client.JSONString; +import com.google.gwt.maps.client.base.LatLng; +import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; +import com.sap.sailing.gwt.ui.shared.WindFieldDTO; +import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO; +import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; +import com.sap.sse.gwt.client.player.Timer; + +/** + * A google map overlay based on a HTML5 canvas for drawing a wind field. The overlay covers the whole map and displays + * the wind objects inside it. + * + * @author Nidhi Sawhney(D054070) + * + */ +public class WindStreamletsCanvasOverlayJS extends FullCanvasOverlay implements TimeListenerWithStoppingCriteria { + + /** The wind field that is to be displayed in the overlay */ + protected WindFieldDTO windFieldDTO; + protected WindFieldGenParamsDTO windParams = null; + protected SimulatorMap simulatorMap; + + private boolean visible = false; + private Timer timer; + + private int nParticles; + private Swarm swarm; + + private static Logger logger = Logger.getLogger(WindStreamletsCanvasOverlayJS.class.getName()); + + public WindStreamletsCanvasOverlayJS(SimulatorMap simulatorMap, int zIndex, final Timer timer, final WindFieldGenParamsDTO windParams) { + super(simulatorMap.getMap(), zIndex); + + this.simulatorMap = simulatorMap; + this.timer = timer; + this.windParams = windParams; + + this.nParticles = this.simulatorMap.getMainPanel().particles; + + + windFieldDTO = null; + + getCanvas().getElement().setId("swarm-display"); + + /*if (!this.isSwarmDataExt()) { + // random test windfield + JavaScriptObject swarmData = this.getJSONRandomWindData(); + this.setSwarmData(swarmData); + }*/ + } + + public JavaScriptObject getJSONRandomWindData() { + + JSONObject jsonWindData = new JSONObject(); + + jsonWindData.put("timestamp", new JSONString("11:55 am on March 18, 2014")); + + LatLng boundsSW = LatLng.newInstance(53.854617, 8.159124); + LatLng boundsNE = LatLng.newInstance(55.263922, 11.413824); + + jsonWindData.put("x0", new JSONNumber(boundsSW.getLongitude())); + jsonWindData.put("y0", new JSONNumber(boundsSW.getLatitude())); + jsonWindData.put("x1", new JSONNumber(boundsNE.getLongitude())); + jsonWindData.put("y1", new JSONNumber(boundsNE.getLatitude())); + + int maxSteps = 1; + int gridWidth = 100; + int gridHeight = 100; + jsonWindData.put("gridWidth", new JSONNumber(gridWidth)); + jsonWindData.put("gridHeight", new JSONNumber(gridHeight)); + + JSONArray windField = new JSONArray(); + + for(int stp=0; stp < maxSteps; stp++) { + JSONArray vectorField = new JSONArray(); + for(int idx=0; idx < (gridWidth*gridHeight*2); idx++) { + vectorField.set(idx, new JSONNumber((Math.random()-0.5)*10.0+3.0)); + //windField.set(idx, new JSONNumber(10.0)); + } + windField.set(stp, vectorField); + } + jsonWindData.put("field", windField); + + return jsonWindData.getJavaScriptObject(); + } + + public native boolean isSwarmDataExt() /*-{ + if ($wnd.swarmDataExt) { + return true; + } else { + return false; + } + }-*/; + + public native String getSwarmData() /*-{ + return JSON.stringify($wnd.swarmData); + }-*/; + + public native void setSwarmData(JavaScriptObject swarmData) /*-{ + $wnd.swarmData = swarmData; + }-*/; + + private native void getJSNIWind(WindStreamletsCanvasOverlayJS wsc) /*-{ + $wnd.getWindfromSimulator = function(idx) { + return wsc.@com.sap.sailing.gwt.ui.simulator.WindStreamletsCanvasOverlay::getWind(I)(idx); + }; + }-*/; + + private native JavaScriptObject getWindInst(double x, double y) /*-{ + return {x:x, y:y}; + }-*/; + + public JavaScriptObject getWind(int idx){ + SimulatorWindDTO wind = this.windFieldDTO.getMatrix().get(idx); + double y = wind.trueWindSpeedInKnots*Math.cos(wind.trueWindBearingDeg*Math.PI/180.0); + double x = wind.trueWindSpeedInKnots*Math.sin(wind.trueWindBearingDeg*Math.PI/180.0); + return getWindInst(x,y); + } + + public native void setWindDataJSON(String jsonField) /*-{ + eval(jsonField); + }-*/; + + public WindStreamletsCanvasOverlayJS(SimulatorMap simulatorMap, int zIndex) { + this(simulatorMap, zIndex, null, null); + } + + public native void setMapInstance(Object mapInstance) /*-{ + $wnd.swarmMap = mapInstance; + }-*/; + + public native void setCanvasProjectionInstance(Object instance) /*-{ + $wnd.swarmCanvasProjection = instance; + }-*/; + + public native void startStreamlets() /*-{ + if ($wnd.swarmAnimator) { + $wnd.swarmUpdData = true; + $wnd.updateStreamlets($wnd.swarmUpdData); + } else { + $wnd.initStreamlets($wnd.swarmMap); + } + }-*/; + + public native void setStreamletsStep(int step) /*-{ + $wnd.swarmField.setStep(step); + }-*/; + + public native void stopStreamlets() /*-{ + if ($wnd.stopStreamlets) { + $wnd.stopStreamlets(); + }; + }-*/; + + public void extendWindDataJSON() { + + List data = this.windFieldDTO.getMatrix(); + String jsonData = "data:["; + int p = 0; + int imax = this.windParams.getyRes() + 2*this.windParams.getBorderY(); + int jmax = this.windParams.getxRes() + 2*this.windParams.getBorderX(); + int steps = data.size() / (imax * jmax); + double maxWindSpeed = 0; + for(int s=0; s maxWindSpeed) { + maxWindSpeed = wind.trueWindSpeedInKnots; + } + double y = wind.trueWindSpeedInKnots*Math.cos(wind.trueWindBearingDeg*Math.PI/180.0); + double x = wind.trueWindSpeedInKnots*Math.sin(wind.trueWindBearingDeg*Math.PI/180.0); + jsonData += x + "," + y; + if (j<(jmax-1)) { + jsonData += ","; + } + } + if (i<(imax-1)) { + jsonData += "],"; + } else { + jsonData += "]"; + } + } + if (s<(steps-1)) { + jsonData += "],"; + } else { + jsonData += "]"; + } + } + this.windFieldDTO.windDataJSON += jsonData + "],maxLength:" + maxWindSpeed + "};"; + + } + + + public void setWindField(final WindFieldDTO windFieldDTO) { + this.windFieldDTO = windFieldDTO; + + this.extendWindDataJSON(); + this.setWindDataJSON(this.windFieldDTO.windDataJSON); + + //this.getJSNIWind(this); + + this.setMapInstance(this.simulatorMap.getMap().getJso()); + this.setCanvasProjectionInstance(this.simulatorMap.getRegattaAreaCanvasOverlay().getMapProjection()); + } + + @Override + public boolean isVisible() { + return this.visible; + } + + @Override + public void setVisible(boolean isVisible) { + if (getCanvas() != null) { + if (isVisible) { + this.startStreamlets(); + if (timer.getTime().compareTo(this.windParams.getStartTime()) != 0) { + this.timeChanged(timer.getTime(), null); + } + this.visible = isVisible; + } else { + this.stopStreamlets(); + this.visible = isVisible; + } + } + } + + @Override + public void addToMap() { + if (timer != null) { + timer.addTimeListener(this); + } + } + + @Override + public void removeFromMap() { + if (timer != null) { + timer.removeTimeListener(this); + } + this.setVisible(false); + } + + @Override + protected void drawCenterChanged() { + } + + @Override + protected void draw() { + super.draw(); + if (mapProjection != null) { + if ((nParticles > 0)&&(swarm == null)) { + this.swarm = new Swarm(this,map); + this.swarm.start(40, null); + } + if (windFieldDTO != null) { + // drawing is done by external JavaScript for Streamlets + } + } + } + + public Swarm getSwarm() { + return this.swarm; + } + + private void clear() { + this.stopStreamlets(); + } + + @Override + public void timeChanged(final Date newDate, Date oldDate) { + int step = (int)((newDate.getTime() - this.windParams.getStartTime().getTime()) / this.windParams.getTimeStep().getTime()); + this.setStreamletsStep(step); + } + + @Override + public boolean shallStop() { + if (timer.getTime().getTime() >= this.windParams.getEndTime().getTime()) { + return true; + } else { + return false; + } + } + + public void setTimer(final Timer timer) { + this.timer = timer; + } + + public Timer getTimer() { + return timer; + } +} From 10de5488395819f745be2be1ecc6ec7ab4cb8264 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 6 May 2014 23:57:57 +0200 Subject: [PATCH 11/66] cleanup & create new streamlets subpackage; remove native JS; --- .../js/wind-streamlets.js | 845 ------------------ .../gwt/ui/simulator/SimulatorField.java | 5 + .../WindStreamletsCanvasOverlay.java | 28 +- .../WindStreamletsCanvasOverlayJS.java | 304 ------- .../ui/simulator/{ => streamlets}/GeoPos.java | 2 +- .../ui/simulator/{ => streamlets}/Index.java | 2 +- .../simulator/{ => streamlets}/Mercator.java | 2 +- .../simulator/{ => streamlets}/Neighbors.java | 2 +- .../simulator/{ => streamlets}/Particle.java | 2 +- .../simulator/{ => streamlets}/RectField.java | 2 +- .../ui/simulator/{ => streamlets}/Swarm.java | 9 +- .../ui/simulator/{ => streamlets}/Vector.java | 2 +- .../{ => streamlets}/VectorField.java | 2 +- 13 files changed, 29 insertions(+), 1178 deletions(-) delete mode 100644 java/com.sap.sailing.gwt.ui/js/wind-streamlets.js delete mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlayJS.java rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/{ => streamlets}/GeoPos.java (85%) rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/{ => streamlets}/Index.java (69%) rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/{ => streamlets}/Mercator.java (97%) rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/{ => streamlets}/Neighbors.java (86%) rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/{ => streamlets}/Particle.java (70%) rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/{ => streamlets}/RectField.java (98%) rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/{ => streamlets}/Swarm.java (95%) rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/{ => streamlets}/Vector.java (88%) rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/{ => streamlets}/VectorField.java (92%) diff --git a/java/com.sap.sailing.gwt.ui/js/wind-streamlets.js b/java/com.sap.sailing.gwt.ui/js/wind-streamlets.js deleted file mode 100644 index 901f68b5b09..00000000000 --- a/java/com.sap.sailing.gwt.ui/js/wind-streamlets.js +++ /dev/null @@ -1,845 +0,0 @@ -/** - * Streamlets - * - * Visualizes a vector field using an animated swarm of particles - * where each particle moves along the flow of the vector field - * - * - integrated with Google Maps v3 - * - support vector fields unaligned with lat/lng-grid - * - support time-dependent vector fields - * - integrated with GWT for Strategy Simulator - * - * @author Christopher Ronnewinkel (D036654) - * - */ - -var Vector = function(x, y) { - this.x = x; - this.y = y; -}; - -Vector.prototype.length = function() { - return Math.sqrt(this.x * this.x + this.y * this.y); -}; - -Vector.prototype.setLength = function(length) { - var current = this.length(); - if (current) { - var scale = length / current; - this.x *= scale; - this.y *= scale; - } - return this; -}; - -var Mercator = function(map, canvas, field_opt) { - this.canvas = canvas; - this.alpha = 0.0; - this.beta = 0.0; - this.gamma = 0.0; - this.delta = 0.0; - this.map = map; - if (field_opt) { - this.fieldNW = new google.maps.LatLng(field_opt.y1, field_opt.x0); - this.fieldSE = new google.maps.LatLng(field_opt.y0, field_opt.x1); - } - this.calibrate(); -}; - -Mercator.prototype.calibrate = function() { - - var pointSW; - var pointNE; - - if (this.fieldNW === undefined) { - - var canvasWidth = this.map.getDiv().clientWidth; - var canvasHeight = this.map.getDiv().clientHeight; - - this.canvas.style.width = canvasWidth + "px"; - this.canvas.style.height = canvasHeight + "px"; - this.canvas.width = canvasWidth; - this.canvas.height = canvasHeight; - - var sw = swarmCanvasProjection.fromLatLngToDivPixel(this.map.getBounds() - .getSouthWest()); - var ne = swarmCanvasProjection.fromLatLngToDivPixel(this.map.getBounds() - .getNorthEast()); - - this.canvas.style.position = "absolute"; - this.canvas.style.left = sw.x + "px"; - this.canvas.style.top = ne.y + "px"; - - var mapSW = this.map.getBounds().getSouthWest(); - var mapNE = this.map.getBounds().getNorthEast(); - - pointSW = this.sphere2plane(mapSW.lat(), mapSW.lng()); - pointNE = this.sphere2plane(mapNE.lat(), mapNE.lng()); - - } else { - - var fieldNWpx = swarmCanvasProjection.fromLatLngToDivPixel(this.fieldNW); - var fieldSEpx = swarmCanvasProjection.fromLatLngToDivPixel(this.fieldSE); - - this.canvas.style.left = fieldNWpx.x+"px"; - this.canvas.style.top = fieldNWpx.y+"px"; - var canvasWidth = fieldSEpx.x - fieldNWpx.x; - var canvasHeight = fieldSEpx.y - fieldNWpx.y; - this.canvas.style.width = canvasWidth + "px"; - this.canvas.style.height = canvasHeight + "px"; - this.canvas.width = canvasWidth; - this.canvas.height = canvasHeight; - - pointSW = this.sphere2plane(this.fieldSE.lat(), this.fieldNW.lng()); - pointNE = this.sphere2plane(this.fieldNW.lat(), this.fieldSE.lng()); - - } - - if (pointNE.x < pointSW.x) { - pointSW.x -= 2*Math.PI; - } - - this.alpha = this.canvas.height / (pointNE.y - pointSW.y); - this.beta = pointSW.x; - - this.gamma = - this.alpha; - this.delta = pointNE.y; -}; - -Mercator.prototype.sphere2plane = function(lat, lng) { - var p = lng*Math.PI/180.0; - var latsin = Math.sin(lat*Math.PI/180.0); - var q = 0.5*Math.log((1.0+latsin)/(1.0-latsin)); - return {x:p, y:q}; -}; - -Mercator.prototype.plane2sphere = function(x,y) { - var lng = x*180.0/Math.PI; - var lat = Math.atan(Math.sinh(y))*180.0/Math.PI; - return {lat:lat, lng:lng}; -}; - -Mercator.prototype.latlng2pixel = function(lat, lng) { - var proj = this.sphere2plane(lat,lng); - var x = this.alpha * (proj.x - this.beta); - var y = this.gamma * (proj.y - this.delta); - return {x:x, y:y}; -}; - -Mercator.prototype.pixel2latlng = function(x, y) { - var p = x / this.alpha + this.beta; - var q = y / this.gamma + this.delta; - return this.plane2sphere({x:p, y:q}); -}; - -Mercator.prototype.clearCanvas = function() { - var w = this.canvas.width; - var h = this.canvas.height; - var g = this.canvas.getContext('2d'); - g.clearRect(0, 0, w, h); -}; - -var VectorField = function(data) { - - this.data = data; - this.step = 0; - this.rcStart = data.rcStart; - this.rcEnd = data.rcEnd; - this.resY = data.resY; - this.resX = data.resX; - this.borderY = data.borderY; - this.borderX = data.borderX; - this.bdXi = (data.borderY + 0.5) / (this.resY - 1); - this.bdPhi = 1.0 + 2*this.bdXi; - this.bdA = { lat:this.rcEnd.lat+(this.rcEnd.lat-this.rcStart.lat)*this.bdXi, lng:this.rcEnd.lng+(this.rcEnd.lng-this.rcStart.lng)*this.bdXi }; - this.bdB = { lat:(this.rcStart.lat-this.rcEnd.lat)*this.bdPhi, lng:(this.rcStart.lng-this.rcEnd.lng)*this.bdPhi }; - this.xScale = data.xScale; - this.x0 = data.boundsSW.lng; - this.x1 = data.boundsNE.lng; - this.y0 = data.boundsSW.lat; - this.y1 = data.boundsNE.lat; - this.visX0 = 0; - this.visY0 = 0; - this.visX1 = 0; - this.visY1 = 0; - this.maxLength = data.maxLength; - this.numParticleFactor = 2.0; - - var latAvg = (this.rcEnd.lat + this.rcStart.lat) / 2.; - this.lngScale = Math.cos(latAvg * Math.PI / 180.0); - - var difLat = this.rcEnd.lat - this.rcStart.lat; - var difLng = (this.rcEnd.lng - this.rcStart.lng) * this.lngScale; - var difLen = Math.sqrt(difLat*difLat + difLng*difLng); - this.nvY = { lat: difLat/difLen/difLen*(this.resY-1), lng: difLng/difLen/difLen*(this.resY-1) }; - - var nrmLat = -difLng/difLen; - var nrmLng = difLat/difLen; - this.nvX = { lat: nrmLat/this.xScale/difLen*(this.resX-1), lng: nrmLng/this.xScale/difLen*(this.resX-1) }; - this.gvX = { lat: nrmLat*this.xScale*difLen, lng: nrmLng/this.lngScale*this.xScale*difLen }; - this.bdC = { lat:this.gvX.lat*(this.resX+2*this.borderX-1)/(this.resX-1), lng:this.gvX.lng*(this.resX+2*this.borderX-1)/(this.resX-1)}; -}; - -VectorField.prototype.getRandomPosition = function() { - var rndY = Math.random(); - var rndX = Math.random() - 0.5; - var latP = this.bdA.lat + rndY * this.bdB.lat + rndX * this.bdC.lat; - var lngP = this.bdA.lng + rndY * this.bdB.lng + rndX * this.bdC.lng; - - if (swarmDebug&&(!this.inBounds(lngP, latP))) { - console.log("random-position: out of bounds"); - } - - return {lat:latP, lng:lngP}; -}; - - -VectorField.prototype.inBounds = function(lng, lat) { - var idx = this.getIndex(lat, lng); - var inBool = (idx.x >= 0) && (idx.x < (this.resX+2*this.borderX)) && (idx.y >= 0) && (idx.y < (this.resY+2*this.borderY)); - return inBool; -}; - -VectorField.prototype.interpolate = function(lat, lng) { - - var idx = this.getNeighbors(lat, lng); - - if (swarmDebug&&((idx.xTop >= (this.resX+2*this.borderX))||(idx.yTop >= (this.resY+2*this.borderY)))) { - console.log("interpolate: out of range: " + idx.xTop + " " + idx.yTop); - } - - var avgX = this.data.data[this.step][idx.yBot][2*idx.xBot] * (1 - idx.yMod) * (1 - idx.xMod) + this.data.data[this.step][idx.yTop][2*idx.xBot] * idx.yMod * (1 - idx.xMod) - + this.data.data[this.step][idx.yBot][2*idx.xTop] * (1 - idx.yMod) * idx.xMod + this.data.data[this.step][idx.yTop][2*idx.xTop] * idx.yMod * idx.xMod; - var avgY = this.data.data[this.step][idx.yBot][2*idx.xBot+1] * (1 - idx.yMod) * (1 - idx.xMod) + this.data.data[this.step][idx.yTop][2*idx.xBot+1] * idx.yMod * (1 - idx.xMod) - + this.data.data[this.step][idx.yBot][2*idx.xTop+1] * (1 - idx.yMod) * idx.xMod + this.data.data[this.step][idx.yTop][2*idx.xTop+1] * idx.yMod * idx.xMod; - - return {x:avgX / this.lngScale, y:avgY}; -}; - -VectorField.prototype.interpolate2 = function(lat, lng) { - - var idx = this.getNeighbors(lat, lng); - - if (swarmDebug&&((idx.xTop >= (this.resX+2*this.borderX))||(idx.yTop >= (this.resY+2*this.borderY)))) { - console.log("interpolate: out of range: " + idx.xTop + " " + idx.yTop); - } - - var idxBase = this.step*this.resX*this.resY; - var yBotxBot = getWindfromSimulator(idxBase + idx.yBot*this.resY + idx.xBot); - var yTopxBot = getWindfromSimulator(idxBase + idx.yTop*this.resY + idx.xBot); - var yBotxTop = getWindfromSimulator(idxBase + idx.yBot*this.resY + idx.xTop); - var yTopxTop = getWindfromSimulator(idxBase + idx.yTop*this.resY + idx.xTop); - - var avgX = yBotxBot.x * (1 - idx.yMod) * (1 - idx.xMod) + yTopxBot.x * idx.yMod * (1 - idx.xMod) + yBotxTop.x * (1 - idx.yMod) * idx.xMod + yTopxTop.x * idx.yMod * idx.xMod; - var avgY = yBotxBot.y * (1 - idx.yMod) * (1 - idx.xMod) + yTopxBot.y * idx.yMod * (1 - idx.xMod) + yBotxTop.y * (1 - idx.yMod) * idx.xMod + yTopxTop.y * idx.yMod * idx.xMod; - - return {x:avgX / this.lngScale, y:avgY}; -}; - -VectorField.prototype.setStep = function(step) { - if (step < 0) { - this.step = 0; - } else if (step >= this.data.data.length) { - this.step = this.data.data.length-1; - } else { - this.step = step; - } -}; - -VectorField.prototype.nextStep = function() { - if (this.step < (this.data.data.length-1)) { - this.step++; - } -}; - -VectorField.prototype.prevStep = function() { - if (this.step > 0) { - this.step--; - } -}; - -VectorField.prototype.getValue = function(lat, lng, opt_result) { - - var v = this.interpolate(lat, lng); - if (opt_result) { - opt_result.x = v.x; - opt_result.y = v.y; - return opt_result; - } - return new Vector(v.x, v.y); -}; - -VectorField.prototype.getValue2 = function(lat, lng, opt_result) { - - var v = this.interpolate(lat, lng); - if (opt_result) { - opt_result.x = v.x; - opt_result.y = v.y; - return opt_result; - } - return new Vector(v.x, v.y); -}; - -VectorField.prototype.getIndex = function(lat, lng) { - - // calculate grid indexes - var posR = { lat: lat - this.rcStart.lat, lng: (lng - this.rcStart.lng) * this.lngScale }; - - // closest grid point - var yIdx = Math.round( posR.lat * this.nvY.lat + posR.lng * this.nvY.lng ) + this.borderY; - var xIdx = Math.round( posR.lat * this.nvX.lat + posR.lng * this.nvX.lng + (this.resX - 1) / 2. ) + this.borderX; - - return { x: xIdx, y: yIdx }; -}; - -VectorField.prototype.getNeighbors = function(lat, lng) { - - // calculate grid indexes - var posR = { lat: lat - this.rcStart.lat, lng: (lng - this.rcStart.lng) * this.lngScale }; - - // surrounding grid points - var yFlt = posR.lat * this.nvY.lat + posR.lng * this.nvY.lng + this.borderY; - var xFlt = posR.lat * this.nvX.lat + posR.lng * this.nvX.lng + (this.resX - 1) / 2. + this.borderX; - var yBot = Math.floor( yFlt ); - var xBot = Math.floor( xFlt ); - var yTop = Math.ceil( yFlt ); - var xTop = Math.ceil( xFlt ); - var yMod = yFlt - yBot; - var xMod = xFlt - xBot; - - if (xBot < 0) { - xBot = 0; - } - - if (yBot < 0) { - yBot = 0; - } - - if (xTop >= (this.resX+2*this.borderX)) { - xTop = this.resX+2*this.borderX-1; - } - - if (yTop >= (this.resY+2*this.borderY)) { - yTop = this.resY+2*this.borderY-1; - } - - return { xTop: xTop, yTop:yTop, xBot:xBot, yBot:yBot, xMod:xMod, yMod:yMod }; -}; - -VectorField.prototype.motionScale = function(zoomLevel) { - return 0.08 * Math.pow(1.6, Math.min(1.0, 6.0 - zoomLevel)); -}; - -VectorField.prototype.particleWeight = function(p,v) { - return v.length() / this.maxLength + 0.1; -}; - -VectorField.prototype.getColors = function() { - var colors = []; - var alpha = 0.7; - var greyValue = 255; - for (var i = 0; i < 256; i++) { - colors[i] = 'rgba(' + (greyValue) + ',' + (greyValue) + ',' + (greyValue) + ',' + (alpha*i/255.0) + ')'; - //this.colors[i] = 'hsla(' + 360*(0.55+0.9*(0.5-i/255)) + ',' + (100) + '% ,' + (50) + '%,' + (i/255) + ')'; - } - return colors; -}; - -VectorField.prototype.lineWidth = function(s) { - return 1.0; -}; - -/** - * for compatibility with vector fields aligned with lat/lng-grid - **/ -var RectField = function(field, x0, y0, x1, y1) { - this.x0 = x0; - this.x1 = x1; - this.y0 = y0; - this.y1 = y1; - - this.visX0 = 0; - this.visY0 = 0; - this.visX1 = 0; - this.visY1 = 0; - - this.field = field; - this.w = field.length; - this.h = field[0].length; - this.maxLength = 0; - this.numParticleFactor = 4.5; - var mx = 0; - var my = 0; - for (var i = 0; i < this.w; i++) { - for (var j = 0; j < this.h; j++) { - if (field[i][j].length() > this.maxLength) { - mx = i; - my = j; - } - this.maxLength = Math.max(this.maxLength, field[i][j].length()); - } - } - mx = (mx / this.w) * (x1 - x0) + x0; - my = (my / this.h) * (y1 - y0) + y0; -}; - -RectField.read = function(data, correctForSphere) { - var field = []; - var w = data.gridWidth; - var h = data.gridHeight; - - var i = 0; - for (var x = 0; x < w; x++) { - field[x] = []; - for (var y = 0; y < h; y++) { - var vx = data.field[i++]; - var vy = data.field[i++]; - var v = new Vector(vx, vy); - if (correctForSphere) { - var uy = y / (h - 1); - var lat = data.y0 * (1 - uy) + data.y1 * uy; - var m = Math.PI * lat / 180; - var length = v.length(); - v.x /= Math.cos(m); - v.setLength(length); - } - field[x][y] = v; - } - } - var result = new RectField(field, data.x0, data.y0, data.x1, data.y1); - return result; -}; - -RectField.prototype.getRandomPosition = function() { - var rndY = Math.random(); - var rndX = Math.random(); - var y = rndY * this.visY0 + (1 - rndY) * this.visY1; - var x = rndX * this.visX0 + (1 - rndX) * this.visX1; - return {lat:y, lng:x}; -}; - -RectField.prototype.inBounds = function(x, y) { - return x >= this.x0 && x < this.x1 && y >= this.y0 && y < this.y1; -}; - -RectField.prototype.interpolate = function(a, b) { - var na = Math.floor(a); - var nb = Math.floor(b); - var ma = Math.ceil(a); - var mb = Math.ceil(b); - var fa = a - na; - var fb = b - nb; - - var avgX = this.field[na][nb]['x'] * (1 - fa) * (1 - fb) + this.field[ma][nb]['x'] * fa * (1 - fb) + this.field[na][mb]['x'] * (1 - fa) * fb + this.field[ma][mb]['x'] * fa * fb; - var avgY = this.field[na][nb]['y'] * (1 - fa) * (1 - fb) + this.field[ma][nb]['y'] * fa * (1 - fb) + this.field[na][mb]['y'] * (1 - fa) * fb + this.field[ma][mb]['y'] * fa * fb; - - return {x:avgX, y:avgY}; -}; - - -RectField.prototype.getValue = function(y, x, opt_result) { - var a = (this.w - 1 - 1e-6) * (x - this.x0) / (this.x1 - this.x0); - var b = (this.h - 1 - 1e-6) * (y - this.y0) / (this.y1 - this.y0); - var v = this.interpolate(a, b); - if (opt_result) { - opt_result.x = v.x; - opt_result.y = v.y; - return opt_result; - } - return new Vector(v.x, v.y); -}; - - -RectField.prototype.motionScale = function(zoomLevel) { - return 0.9 * Math.pow(1.7, Math.min(1.0, 6.0 - zoomLevel)); -}; - -RectField.prototype.particleWeight = function(p,v) { - return 1.0 - v.length() / this.maxLength; -}; - -RectField.prototype.getColors = function() { - var colors = []; - var alpha = 1.0; - var greyValue = 255; - for (var i = 0; i < 256; i++) { - colors[i] = 'rgba(' + (greyValue) + ',' + (greyValue) + ',' + (greyValue) + ',' + (alpha*i/255.0) + ')'; - //this.colors[i] = 'hsla(' + 360*(0.55+0.9*(0.5-i/255)) + ',' + (100) + '% ,' + (50) + '%,' + (i/255) + ')'; - } - return colors; -}; - -RectField.prototype.lineWidth = function(s) { - return 1.0; -}; - -var Animator = function() { - this.animating = true; - this.state = 'animate'; - this.pause = 0; - this.listeners = []; - this.dx = 0; - this.dy = 0; - this.scale = 1; - this.zoomProgress = 0; - this.scaleTarget = 1; - this.scaleStart = 1; -}; - - -Animator.prototype.add = function(listener) { - this.listeners.push(listener); -}; - - -Animator.prototype.notify = function(message) { - if (this.animFunc && !this.animFunc()) { - return; - } - for (var i = 0; i < this.listeners.length; i++) { - var listener = this.listeners[i]; - if (listener[message]) { - listener[message].call(listener, this); - } - } -}; - -Animator.prototype.start = function(opt_millis) { - var millis = opt_millis || 20; - var self = this; - function go() { - var start = new Date(); - if (self.loop()) { - return; - } - var time = new Date() - start; - setTimeout(go, Math.max(10, millis - time)); - } - go(); -}; - - -Animator.prototype.loop = function() { - if (this.state == 'stop') { - return true; - } - if (this.pause > 1) { - this.pause--; - } else if (this.pause == 1){ - this.state = 'animate'; - this.pause = 0; - } - if (this.state == 'animate') { - this.notify('animate'); - return false; - } - if ((this.state == 'pause')&&(this.pause == 0)) { - this.pause = 5; - } - return false; -}; - -var Particle = function(x, y, age) { - this.x = x; - this.y = y; - this.oldX = -1; - this.oldY = -1; - this.age = age; - this.rnd = Math.random(); -}; - -var swarmOffScreen = false; - -var Swarm = function(canvas, field, opt_projection) { - this.canvas = canvas; - this.projection = opt_projection || IDProjection; - this.field = field; - this.numParticles = 0; - this.first = true; - this.maxLength = field.maxLength; - this.speedScale = 1; - this.renderState = 'normal'; - - this.boundsSW = new google.maps.LatLng(this.field.y0, this.field.x0); - this.boundsNE = new google.maps.LatLng(this.field.y1, this.field.x1); - - this.updateBounds(null); - - this.makeNewParticles(null, true); - this.rgb = '40, 40, 40'; - this.background = 'rgb(' + this.rgb + ')'; - this.colors = this.field.getColors(); - - if (this.projection) { - this.startOffsetX = this.projection.offsetX; - this.startOffsetY = this.projection.offsetY; - this.startScale = this.projection.scale; - } -}; - - -Swarm.prototype.updateBounds = function() { - - var mapNE = this.projection.map.getBounds().getNorthEast(); - var mapSW = this.projection.map.getBounds().getSouthWest(); - - var fieldNE = new google.maps.LatLng(Math.max(this.field.y0, this.field.y1), Math.max(this.field.x0, this.field.x1)); - var fieldSW = new google.maps.LatLng(Math.min(this.field.y0, this.field.y1), Math.min(this.field.x0, this.field.x1)); - - var visibleNE = this.isVisible(fieldNE); - var visibleSW = this.isVisible(fieldSW); - - var useBoundsNorth = (visibleNE.y == 0); - var useBoundsEast = (visibleNE.x == 0); - var useBoundsSouth = (visibleSW.y == 0); - var useBoundsWest = (visibleSW.x == 0); - - swarmOffScreen = (visibleNE.y > 0)||(visibleSW.y < 0)||(visibleNE.x < 0)||(visibleSW.x > 0); - - if (swarmOffScreen) { - - this.boundsNE = fieldNE; - this.boundsSW = fieldSW; - - } else { - - if ((!useBoundsNorth)&&(!useBoundsEast)) { - this.boundsNE = mapNE; - } else if (!useBoundsNorth) { - this.boundsNE = new google.maps.LatLng(mapNE.lat(), fieldNE.lng()); - } else if (!useBoundsEast) { - this.boundsNE = new google.maps.LatLng(fieldNE.lat(), mapNE.lng()); - } else { - this.boundsNE = fieldNE; - } - - if ((!useBoundsSouth)&&(!useBoundsWest)) { - this.boundsSW = mapSW; - } else if (!useBoundsSouth) { - this.boundsSW = new google.maps.LatLng(mapSW.lat(), fieldSW.lng()); - } else if (!useBoundsWest) { - this.boundsSW = new google.maps.LatLng(fieldSW.lat(), mapSW.lng()); - } else { - this.boundsSW = fieldSW; - } - - } - - this.field.visX0 = this.boundsSW.lng(); - this.field.visY0 = this.boundsSW.lat(); - this.field.visX1 = this.boundsNE.lng(); - this.field.visY1 = this.boundsNE.lat(); - - var boundsSWpx = this.projection.latlng2pixel(this.boundsSW.lat(), this.boundsSW.lng()); - var boundsNEpx = this.projection.latlng2pixel(this.boundsNE.lat(), this.boundsNE.lng()); - - var boundsWidthpx = Math.abs(boundsNEpx.x - boundsSWpx.x); - var boundsHeightpx = Math.abs(boundsSWpx.y - boundsNEpx.y); - - this.numParticles = Math.round(Math.sqrt(boundsWidthpx * boundsHeightpx) * this.field.numParticleFactor); - //this.numParticles = Math.sqrt(boundsWidthpx*boundsWidthpx + boundsHeightpx*boundsHeightpx) * this.field.numParticleFactor; - if (swarmDebug) { - console.log("numParticles: "+this.numParticles + " at " + (boundsWidthpx) +"x" + (boundsHeightpx) + "px (" + (boundsWidthpx * boundsHeightpx) + " pixels)"); - } -}; - -Swarm.prototype.isVisible = function(pos) { - - // test for visibility of swarm - var proj = this.projection.latlng2pixel(pos.lat(), pos.lng()); - - var xVisible = -(proj.x < 0) + (proj.x > this.canvas.width); - var yVisible = -(proj.y < 0) + (proj.y > this.canvas.height); - - return {x:xVisible, y:yVisible}; -}; - -Swarm.prototype.makeNewParticles = function() { - this.particles = []; - for (var i = 0; i < this.numParticles; i++) { - particle = this.makeParticle(); - if (particle) { - this.particles.push(particle); - } else { - break; - } - } -}; - -Swarm.prototype.makeParticle = function() { - var trials = 0; - while (true) { - var pos = this.field.getRandomPosition(); - var x = pos.lng; - var y = pos.lat; - var v = this.field.getValue(y, x); - if (this.field.maxLength == 0) { - return new Particle(x, y, 1 + 40 * Math.random()); - } - var weight = this.field.particleWeight(pos, v); - if (weight >= Math.random()) { - var proj = this.projection.latlng2pixel(y, x); - if (++trials > 10 || !(proj.x < 0 || proj.y < 0 || proj.x > this.canvas.width || proj.y > this.canvas.height)) { - if (v.x || v.y) { - return new Particle(x, y, 1 + 40 * Math.random()); - } else { - return new Particle(x, y, 0); // "zombie" particles to avoid dead-lock when Google Maps is moved so that only zero-wind-area is visible - } - } - } - } -}; - - -Swarm.prototype.endMove = function() { - this.updateBounds(); - this.makeNewParticles(); -}; - - -Swarm.prototype.animate = function() { - if (!swarmOffScreen) { - this.moveThings(); - this.draw(); - } -}; - - -Swarm.prototype.moveThings = function() { - var speed = .01 * this.speedScale * this.field.motionScale(this.projection.map.getZoom()); - for (var i = 0; i < this.particles.length; i++) { - var p = this.particles[i]; - if (p.age > 0 && this.field.inBounds(p.x, p.y)) { - var a = this.field.getValue(p.y, p.x); - p.x += speed * a.x; - p.y += speed * a.y; - p.age--; - } else { - this.particles[i] = this.makeParticle(); - } - } -}; - - -Swarm.prototype.draw = function() { - var g = this.canvas.getContext('2d'); - var w = this.canvas.width; - var h = this.canvas.height; - - g.globalCompositeOperation = "destination-out"; - g.globalAlpha = 0.08; - g.fillStyle = this.background; - - g.fillRect(0, 0, w, h); - - g.globalCompositeOperation = "source-over"; - g.globalAlpha = 1.0; - - var proj; // = new Vector(0, 0); - var val = new Vector(0, 0); - for (var i = 0; i < this.particles.length; i++) { - var p = this.particles[i]; - if (!this.field.inBounds(p.x, p.y)) { - p.age = -2; - continue; - } - proj = this.projection.latlng2pixel(p.y, p.x); - if (proj.x < 0 || proj.y < 0 || proj.x > w || proj.y > h) { - p.age = -2; - } - if (p.oldX != -1) { - var wind = this.field.getValue(p.y, p.x, val); - var s = wind.length() / this.maxLength; - var c = 90 + Math.round(350 * s); // was 400 - if (c > 255) { - c = 255; - } - g.globalAlpha = 1.0; - g.lineWidth = this.field.lineWidth(s); - g.strokeStyle = this.colors[c]; - g.beginPath(); - g.moveTo(proj.x, proj.y); - g.lineTo(p.oldX, p.oldY); - g.stroke(); - g.globalAlpha = 1.0; - } - p.oldX = proj.x; - p.oldY = proj.y; - } -}; - -var swarmData; -var swarmField; -var swarmAnimator; -var swarmProjection; -var swarmDebug = false; - -function initStreamlets(map, canvas) { - - if (!map) { - alert("Google Maps Instance required."); - return; - } - if (!canvas) { - canvas = document.getElementById('swarm-display'); - if (!canvas) { - alert("canvas 'swarm-display' not available"); - } - } - - if ("windFieldSim" in window) { - - swarmField = new VectorField(windFieldSim); - - } else { - - if ("swarmDataExt" in window) { - swarmData = swarmDataExt; - - map.setZoom(5.0); - var center = new google.maps.LatLng((swarmData.y0+swarmData.y1)/2., (swarmData.x0+swarmData.x1)/2.); - map.panTo(center); - - }; - - swarmField = RectField.read(swarmData, false); - - }; - - swarmProjection = new Mercator(map, canvas); - - var swarm = new Swarm(canvas, swarmField, swarmProjection); - - swarmAnimator = new Animator(); - swarmAnimator.add(swarm); - - updateStreamlets = function(upd_data) { - if (!(upd_data === undefined)) { - swarmAnimator.state = 'stop'; - swarmAnimator = new Animator(); - swarmField = new VectorField(windFieldSim); - swarm = new Swarm(canvas, swarmField, swarmProjection); - swarmAnimator.add(swarm); - swarmAnimator.start(40); - } else if (swarmAnimator.state == 'stop') { - return; - } - swarmAnimator.state = 'pause'; - swarmProjection.clearCanvas(); - swarmProjection.calibrate(); - swarmAnimator.notify('endMove'); - }; - - stopStreamlets = function() { - swarmAnimator.state = 'stop'; - swarmProjection.clearCanvas(); - }; - - if (swarmProjection.fieldNW === undefined) { - google.maps.event.addListener(map, 'bounds_changed', updateStreamlets); - } else { - google.maps.event.addListener(map, 'zoom_changed', updateStreamlets); - } - - swarmAnimator.start(40); -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorField.java index 5ef1385938d..6a7277d0707 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorField.java @@ -7,6 +7,11 @@ import com.google.gwt.json.client.JSONParser; import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO; +import com.sap.sailing.gwt.ui.simulator.streamlets.GeoPos; +import com.sap.sailing.gwt.ui.simulator.streamlets.Index; +import com.sap.sailing.gwt.ui.simulator.streamlets.Neighbors; +import com.sap.sailing.gwt.ui.simulator.streamlets.Vector; +import com.sap.sailing.gwt.ui.simulator.streamlets.VectorField; public class SimulatorField implements VectorField { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index 96af2f85ba2..9e1cb128c2d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -14,6 +14,7 @@ import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO; import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; +import com.sap.sailing.gwt.ui.simulator.streamlets.Swarm; import com.sap.sse.gwt.client.player.Timer; /** @@ -54,6 +55,14 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti } + public WindFieldDTO getWindFieldDTO() { + return this.windFieldDTO; + } + + public WindFieldGenParamsDTO getWindParams() { + return this.windParams; + } + public JavaScriptObject getJSONRandomWindData() { JSONObject jsonWindData = new JSONObject(); @@ -138,15 +147,6 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti $wnd.swarmCanvasProjection = instance; }-*/; -// public native void startStreamlets() /*-{ -// if ($wnd.swarmAnimator) { -// $wnd.swarmUpdData = true; -// $wnd.updateStreamlets($wnd.swarmUpdData); -// } else { -// $wnd.initStreamlets($wnd.swarmMap); -// } -// }-*/; - public void startStreamlets() { if (swarm == null) { this.swarm = new Swarm(this,map); @@ -154,20 +154,10 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti this.swarm.start(40, windFieldDTO); } -// public native void setStreamletsStep(int step) /*-{ -// $wnd.swarmField.setStep(step); -// }-*/; - public void setStreamletsStep(int step) { this.swarm.getField().setStep(step); } -// public native void stopStreamlets() /*-{ -// if ($wnd.stopStreamlets) { -// $wnd.stopStreamlets(); -// }; -// }-*/; - public void stopStreamlets() { if (swarm != null) { this.swarm.stop(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlayJS.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlayJS.java deleted file mode 100644 index 6ad8d586eee..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlayJS.java +++ /dev/null @@ -1,304 +0,0 @@ -package com.sap.sailing.gwt.ui.simulator; - -import java.util.Date; -import java.util.List; -import java.util.logging.Logger; - -import com.google.gwt.core.client.JavaScriptObject; -import com.google.gwt.json.client.JSONArray; -import com.google.gwt.json.client.JSONNumber; -import com.google.gwt.json.client.JSONObject; -import com.google.gwt.json.client.JSONString; -import com.google.gwt.maps.client.base.LatLng; -import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; -import com.sap.sailing.gwt.ui.shared.WindFieldDTO; -import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO; -import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; -import com.sap.sse.gwt.client.player.Timer; - -/** - * A google map overlay based on a HTML5 canvas for drawing a wind field. The overlay covers the whole map and displays - * the wind objects inside it. - * - * @author Nidhi Sawhney(D054070) - * - */ -public class WindStreamletsCanvasOverlayJS extends FullCanvasOverlay implements TimeListenerWithStoppingCriteria { - - /** The wind field that is to be displayed in the overlay */ - protected WindFieldDTO windFieldDTO; - protected WindFieldGenParamsDTO windParams = null; - protected SimulatorMap simulatorMap; - - private boolean visible = false; - private Timer timer; - - private int nParticles; - private Swarm swarm; - - private static Logger logger = Logger.getLogger(WindStreamletsCanvasOverlayJS.class.getName()); - - public WindStreamletsCanvasOverlayJS(SimulatorMap simulatorMap, int zIndex, final Timer timer, final WindFieldGenParamsDTO windParams) { - super(simulatorMap.getMap(), zIndex); - - this.simulatorMap = simulatorMap; - this.timer = timer; - this.windParams = windParams; - - this.nParticles = this.simulatorMap.getMainPanel().particles; - - - windFieldDTO = null; - - getCanvas().getElement().setId("swarm-display"); - - /*if (!this.isSwarmDataExt()) { - // random test windfield - JavaScriptObject swarmData = this.getJSONRandomWindData(); - this.setSwarmData(swarmData); - }*/ - } - - public JavaScriptObject getJSONRandomWindData() { - - JSONObject jsonWindData = new JSONObject(); - - jsonWindData.put("timestamp", new JSONString("11:55 am on March 18, 2014")); - - LatLng boundsSW = LatLng.newInstance(53.854617, 8.159124); - LatLng boundsNE = LatLng.newInstance(55.263922, 11.413824); - - jsonWindData.put("x0", new JSONNumber(boundsSW.getLongitude())); - jsonWindData.put("y0", new JSONNumber(boundsSW.getLatitude())); - jsonWindData.put("x1", new JSONNumber(boundsNE.getLongitude())); - jsonWindData.put("y1", new JSONNumber(boundsNE.getLatitude())); - - int maxSteps = 1; - int gridWidth = 100; - int gridHeight = 100; - jsonWindData.put("gridWidth", new JSONNumber(gridWidth)); - jsonWindData.put("gridHeight", new JSONNumber(gridHeight)); - - JSONArray windField = new JSONArray(); - - for(int stp=0; stp < maxSteps; stp++) { - JSONArray vectorField = new JSONArray(); - for(int idx=0; idx < (gridWidth*gridHeight*2); idx++) { - vectorField.set(idx, new JSONNumber((Math.random()-0.5)*10.0+3.0)); - //windField.set(idx, new JSONNumber(10.0)); - } - windField.set(stp, vectorField); - } - jsonWindData.put("field", windField); - - return jsonWindData.getJavaScriptObject(); - } - - public native boolean isSwarmDataExt() /*-{ - if ($wnd.swarmDataExt) { - return true; - } else { - return false; - } - }-*/; - - public native String getSwarmData() /*-{ - return JSON.stringify($wnd.swarmData); - }-*/; - - public native void setSwarmData(JavaScriptObject swarmData) /*-{ - $wnd.swarmData = swarmData; - }-*/; - - private native void getJSNIWind(WindStreamletsCanvasOverlayJS wsc) /*-{ - $wnd.getWindfromSimulator = function(idx) { - return wsc.@com.sap.sailing.gwt.ui.simulator.WindStreamletsCanvasOverlay::getWind(I)(idx); - }; - }-*/; - - private native JavaScriptObject getWindInst(double x, double y) /*-{ - return {x:x, y:y}; - }-*/; - - public JavaScriptObject getWind(int idx){ - SimulatorWindDTO wind = this.windFieldDTO.getMatrix().get(idx); - double y = wind.trueWindSpeedInKnots*Math.cos(wind.trueWindBearingDeg*Math.PI/180.0); - double x = wind.trueWindSpeedInKnots*Math.sin(wind.trueWindBearingDeg*Math.PI/180.0); - return getWindInst(x,y); - } - - public native void setWindDataJSON(String jsonField) /*-{ - eval(jsonField); - }-*/; - - public WindStreamletsCanvasOverlayJS(SimulatorMap simulatorMap, int zIndex) { - this(simulatorMap, zIndex, null, null); - } - - public native void setMapInstance(Object mapInstance) /*-{ - $wnd.swarmMap = mapInstance; - }-*/; - - public native void setCanvasProjectionInstance(Object instance) /*-{ - $wnd.swarmCanvasProjection = instance; - }-*/; - - public native void startStreamlets() /*-{ - if ($wnd.swarmAnimator) { - $wnd.swarmUpdData = true; - $wnd.updateStreamlets($wnd.swarmUpdData); - } else { - $wnd.initStreamlets($wnd.swarmMap); - } - }-*/; - - public native void setStreamletsStep(int step) /*-{ - $wnd.swarmField.setStep(step); - }-*/; - - public native void stopStreamlets() /*-{ - if ($wnd.stopStreamlets) { - $wnd.stopStreamlets(); - }; - }-*/; - - public void extendWindDataJSON() { - - List data = this.windFieldDTO.getMatrix(); - String jsonData = "data:["; - int p = 0; - int imax = this.windParams.getyRes() + 2*this.windParams.getBorderY(); - int jmax = this.windParams.getxRes() + 2*this.windParams.getBorderX(); - int steps = data.size() / (imax * jmax); - double maxWindSpeed = 0; - for(int s=0; s maxWindSpeed) { - maxWindSpeed = wind.trueWindSpeedInKnots; - } - double y = wind.trueWindSpeedInKnots*Math.cos(wind.trueWindBearingDeg*Math.PI/180.0); - double x = wind.trueWindSpeedInKnots*Math.sin(wind.trueWindBearingDeg*Math.PI/180.0); - jsonData += x + "," + y; - if (j<(jmax-1)) { - jsonData += ","; - } - } - if (i<(imax-1)) { - jsonData += "],"; - } else { - jsonData += "]"; - } - } - if (s<(steps-1)) { - jsonData += "],"; - } else { - jsonData += "]"; - } - } - this.windFieldDTO.windDataJSON += jsonData + "],maxLength:" + maxWindSpeed + "};"; - - } - - - public void setWindField(final WindFieldDTO windFieldDTO) { - this.windFieldDTO = windFieldDTO; - - this.extendWindDataJSON(); - this.setWindDataJSON(this.windFieldDTO.windDataJSON); - - //this.getJSNIWind(this); - - this.setMapInstance(this.simulatorMap.getMap().getJso()); - this.setCanvasProjectionInstance(this.simulatorMap.getRegattaAreaCanvasOverlay().getMapProjection()); - } - - @Override - public boolean isVisible() { - return this.visible; - } - - @Override - public void setVisible(boolean isVisible) { - if (getCanvas() != null) { - if (isVisible) { - this.startStreamlets(); - if (timer.getTime().compareTo(this.windParams.getStartTime()) != 0) { - this.timeChanged(timer.getTime(), null); - } - this.visible = isVisible; - } else { - this.stopStreamlets(); - this.visible = isVisible; - } - } - } - - @Override - public void addToMap() { - if (timer != null) { - timer.addTimeListener(this); - } - } - - @Override - public void removeFromMap() { - if (timer != null) { - timer.removeTimeListener(this); - } - this.setVisible(false); - } - - @Override - protected void drawCenterChanged() { - } - - @Override - protected void draw() { - super.draw(); - if (mapProjection != null) { - if ((nParticles > 0)&&(swarm == null)) { - this.swarm = new Swarm(this,map); - this.swarm.start(40, null); - } - if (windFieldDTO != null) { - // drawing is done by external JavaScript for Streamlets - } - } - } - - public Swarm getSwarm() { - return this.swarm; - } - - private void clear() { - this.stopStreamlets(); - } - - @Override - public void timeChanged(final Date newDate, Date oldDate) { - int step = (int)((newDate.getTime() - this.windParams.getStartTime().getTime()) / this.windParams.getTimeStep().getTime()); - this.setStreamletsStep(step); - } - - @Override - public boolean shallStop() { - if (timer.getTime().getTime() >= this.windParams.getEndTime().getTime()) { - return true; - } else { - return false; - } - } - - public void setTimer(final Timer timer) { - this.timer = timer; - } - - public Timer getTimer() { - return timer; - } -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/GeoPos.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/GeoPos.java similarity index 85% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/GeoPos.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/GeoPos.java index e2ab76d3db3..44c6762d590 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/GeoPos.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/GeoPos.java @@ -1,4 +1,4 @@ -package com.sap.sailing.gwt.ui.simulator; +package com.sap.sailing.gwt.ui.simulator.streamlets; import com.google.gwt.maps.client.base.LatLng; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Index.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Index.java similarity index 69% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Index.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Index.java index 05fd28816b7..59b47daac3b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Index.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Index.java @@ -1,4 +1,4 @@ -package com.sap.sailing.gwt.ui.simulator; +package com.sap.sailing.gwt.ui.simulator.streamlets; public class Index { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Mercator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Mercator.java similarity index 97% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Mercator.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Mercator.java index e57ab0b3087..2a97e4e6f8d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Mercator.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Mercator.java @@ -1,4 +1,4 @@ -package com.sap.sailing.gwt.ui.simulator; +package com.sap.sailing.gwt.ui.simulator.streamlets; import com.google.gwt.canvas.dom.client.Context2d; import com.google.gwt.maps.client.MapWidget; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Neighbors.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Neighbors.java similarity index 86% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Neighbors.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Neighbors.java index 2ffbb5ef913..22bf7c67037 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Neighbors.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Neighbors.java @@ -1,4 +1,4 @@ -package com.sap.sailing.gwt.ui.simulator; +package com.sap.sailing.gwt.ui.simulator.streamlets; public class Neighbors { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java similarity index 70% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java index 79d9e6de302..df819832b8d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Particle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java @@ -1,4 +1,4 @@ -package com.sap.sailing.gwt.ui.simulator; +package com.sap.sailing.gwt.ui.simulator.streamlets; public class Particle { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java similarity index 98% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java index 6613cfab278..4b6569ade5c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/RectField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java @@ -1,4 +1,4 @@ -package com.sap.sailing.gwt.ui.simulator; +package com.sap.sailing.gwt.ui.simulator.streamlets; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONParser; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java similarity index 95% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java index 1b7609e7594..344907a9363 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java @@ -1,4 +1,4 @@ -package com.sap.sailing.gwt.ui.simulator; +package com.sap.sailing.gwt.ui.simulator.streamlets; import java.util.Date; @@ -8,6 +8,9 @@ import com.google.gwt.core.client.GWT; import com.google.gwt.maps.client.MapWidget; import com.google.gwt.user.client.Timer; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; +import com.sap.sailing.gwt.ui.simulator.SimulatorField; +import com.sap.sailing.gwt.ui.simulator.SimulatorJSBundle; +import com.sap.sailing.gwt.ui.simulator.WindStreamletsCanvasOverlay; import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; public class Swarm { @@ -51,7 +54,7 @@ public class Swarm { } else { - field = new SimulatorField(((WindStreamletsCanvasOverlay)fullcanvas).windFieldDTO, ((WindStreamletsCanvasOverlay)fullcanvas).windParams); + field = new SimulatorField(((WindStreamletsCanvasOverlay)fullcanvas).getWindFieldDTO(), ((WindStreamletsCanvasOverlay)fullcanvas).getWindParams()); fullcanvas.setCanvasSettings(); projection.calibrate(); @@ -220,6 +223,8 @@ public class Swarm { //console("delta:"+(time1.getTime()-time0.getTime())+"/"+millis); if (swarmContinue) { loopTimer.schedule((int)Math.max(10, millis - (time1.getTime()-time0.getTime()))); + } else { + projection.clearCanvas(); } } }; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Vector.java similarity index 88% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Vector.java index 42118aa0cc1..6eb1de26712 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/Vector.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Vector.java @@ -1,4 +1,4 @@ -package com.sap.sailing.gwt.ui.simulator; +package com.sap.sailing.gwt.ui.simulator.streamlets; public class Vector { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/VectorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java similarity index 92% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/VectorField.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java index 9236f3ca594..e1b92e95bfb 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/VectorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java @@ -1,4 +1,4 @@ -package com.sap.sailing.gwt.ui.simulator; +package com.sap.sailing.gwt.ui.simulator.streamlets; public interface VectorField { From 3082e3a0ea604d16865eacb328e4a89855a9cad2 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sun, 11 May 2014 21:42:47 +0000 Subject: [PATCH 12/66] Honored the merge of ftes-rltracking into master in racelog tracking Wiki --- wiki/racelog-tracking.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wiki/racelog-tracking.md b/wiki/racelog-tracking.md index f64e06609e5..0a735749109 100644 --- a/wiki/racelog-tracking.md +++ b/wiki/racelog-tracking.md @@ -8,7 +8,7 @@ This is the second iteration of implementing this idea. For previous documentati [[_TOC_]] # Development Infos -Currently, RaceLog tracking has not been merged into the master branch. Instead, refer to the following branches: +RaceLog tracking has been merged into the master branch with commit 4b4b61b6c518139f4389aabd73c282621711855a. It was developed on the following branches: * ``ftes-rltracking``: The main RaceLog-tracking development * ``ftes-rltracking-equestrian``: Serves as a reference implementation of a tracking adapter built for RaceLog-tracking. From b531218338df9322319f50672a18f77da846ddf7 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sun, 11 May 2014 22:11:59 +0000 Subject: [PATCH 13/66] In racelog tracking wiki, described how to obtain the GPSFixStore into which to deliver the GPS fixes when creating an adapter --- wiki/racelog-tracking.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wiki/racelog-tracking.md b/wiki/racelog-tracking.md index 0a735749109..4fe725e8b74 100644 --- a/wiki/racelog-tracking.md +++ b/wiki/racelog-tracking.md @@ -76,5 +76,5 @@ Steps for creating an adapter: 4. Add the bundle to the ``com.sap.sailing.feature/feature.xml``, and also to the ``com.sap.sailing.feature.p2build/raceanalysis.product`` with the same startup properties as before. 5. Implement a ``DeviceIdentifier`` and the appropriate ``DeviceIdentifierSerializationHandler``s (refer to the JavaDoc of ``DeviceIdentifier``). 6. Register your serialization handlers through the OSGi service registry (refer to the JavaDoc of ``DeviceIdentifierSerializationHandler`` for an example on how to do so). -7. Add some kind of Servlet or similar that adds fixes to the GPSFixStore by calling ``storeFix(DeviceIdentifier device, GPSFix fix)``. +7. Add some kind of Servlet or similar that adds fixes to the GPSFixStore by calling ``storeFix(DeviceIdentifier device, GPSFix fix)`` on the `GPSFixStore` which can be obtained from `RacingEventService.getGPSFixStore()`. 8. Fire up the server, let OSGi work its magic, define the race metadata and start adding fixes. \ No newline at end of file From ad8cabe584c29349f6a737b09819421620f20c72 Mon Sep 17 00:00:00 2001 From: fmittag Date: Mon, 12 May 2014 18:30:24 +0200 Subject: [PATCH 14/66] added 'penaltyCircles' to gateway service for competitor leg information --- .../sap/sailing/server/gateway/jaxrs/api/RegattasResource.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java index 4144cc2fe43..f13653141dd 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -599,8 +599,10 @@ public class RegattasResource extends AbstractSailingServerResource { try { Integer numberOfTacks = trackedLegOfCompetitor.getNumberOfTacks(timePoint); Integer numberOfJibes = trackedLegOfCompetitor.getNumberOfJibes(timePoint); + Integer numberOfPenaltyCircles = trackedLegOfCompetitor.getNumberOfPenaltyCircles(timePoint); jsonCompetitorInLeg.put("tacks", numberOfTacks); jsonCompetitorInLeg.put("jibes", numberOfJibes); + jsonCompetitorInLeg.put("penaltyCircles", numberOfPenaltyCircles); } catch (NoWindException e) { } From ebbd963847883ab381759279ca67f4519bd2cd80 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 12 May 2014 21:28:47 +0200 Subject: [PATCH 15/66] trying to fix bug 1947 (NPE with RaceLogTrackingState when there is no RaceLog attached to a column) --- .../java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index bd650387eb1..7e3544cfb65 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -2099,7 +2099,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S raceColumn.getFactor(), fleetDTO, raceColumn.isMedalRace(), raceIdentifier, raceDTO); RaceLog raceLog = raceColumn.getRaceLog(fleet); - RaceLogTrackingState raceLogTrackingState = new RaceLogTrackingStateAnalyzer(raceLog).analyze(); + RaceLogTrackingState raceLogTrackingState = raceLog == null ? RaceLogTrackingState.NOT_A_RACELOG_TRACKED_RACE : + new RaceLogTrackingStateAnalyzer(raceLog).analyze(); boolean raceLogTrackerExists = getService().getRaceTrackerById(raceLog.getId()) != null; boolean competitorRegistrationsExist = ! new RegisteredCompetitorsAnalyzer(raceLog).analyze().isEmpty(); RaceLogTrackingInfoDTO raceLogTrackingInfo = new RaceLogTrackingInfoDTO(raceLogTrackerExists, From b1ff618eefd3361284648ebcbc32798115da1ff6 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 12 May 2014 22:00:14 +0200 Subject: [PATCH 16/66] fixed more NPEs in context of bug 1947 --- .../com/sap/sailing/gwt/ui/server/SailingServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 7e3544cfb65..ac00244f393 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -2101,8 +2101,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S RaceLog raceLog = raceColumn.getRaceLog(fleet); RaceLogTrackingState raceLogTrackingState = raceLog == null ? RaceLogTrackingState.NOT_A_RACELOG_TRACKED_RACE : new RaceLogTrackingStateAnalyzer(raceLog).analyze(); - boolean raceLogTrackerExists = getService().getRaceTrackerById(raceLog.getId()) != null; - boolean competitorRegistrationsExist = ! new RegisteredCompetitorsAnalyzer(raceLog).analyze().isEmpty(); + boolean raceLogTrackerExists = raceLog == null ? false : getService().getRaceTrackerById(raceLog.getId()) != null; + boolean competitorRegistrationsExist = raceLog == null ? false : !new RegisteredCompetitorsAnalyzer(raceLog).analyze().isEmpty(); RaceLogTrackingInfoDTO raceLogTrackingInfo = new RaceLogTrackingInfoDTO(raceLogTrackerExists, competitorRegistrationsExist, raceLogTrackingState); raceColumnDTO.setRaceLogTrackingInfo(fleetDTO, raceLogTrackingInfo); From e39836cdb3b78e08e55673f7b42e03061e1f28b3 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 12 May 2014 23:45:33 +0200 Subject: [PATCH 17/66] working on old bug 171; when one connection in a whole series fails, subsequent connections were affected; resolved these dependencies --- .../impl/SailMasterConnectorImpl.java | 11 ++++++++--- .../impl/SailMasterMessageImpl.java | 6 +++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterConnectorImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterConnectorImpl.java index 862a338fc13..7c356bc833f 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterConnectorImpl.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterConnectorImpl.java @@ -518,8 +518,9 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement SailMasterMessage opnResponse = new SailMasterMessageImpl(receiveMessage(is)); if (opnResponse.getType() != MessageType.OPN || !"OK".equals(opnResponse.getSections()[1])) { logger.info("Recevied non-OK response " + opnResponse + " in "+this+" for our request " + opnRequest - + ". Closing socket and trying again in 1s..."); - closeAndNullSocketAndWaitABit(); + + ". Closing socket and stopping because we have no hope for recovery"); + stopped = true; + closeSocket(); } else { logger.info("Received " + opnResponse + " in "+this+" which seems OK. Continuing with " + MessageType.LSN.name() + " request..."); @@ -553,6 +554,11 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement } private void closeAndNullSocketAndWaitABit() throws InterruptedException { + closeSocket(); + Thread.sleep(1000); + } + + private void closeSocket() { if (socket != null) { try { socket.close(); @@ -561,7 +567,6 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement } socket = null; } - Thread.sleep(1000); } @Override diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterMessageImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterMessageImpl.java index f51285157c7..b98dab16877 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterMessageImpl.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterMessageImpl.java @@ -68,7 +68,11 @@ public class SailMasterMessageImpl implements SailMasterMessage { } else { typeAsString = typeAsStringWithOptionalSuffix.substring(0, typeAsStringWithOptionalSuffix.length()-1); } - return MessageType.valueOf(typeAsString); + try { + return MessageType.valueOf(typeAsString); + } catch (IllegalArgumentException e) { + return null; + } } @Override From b4c2202fd444061894ba7d083d9d97cb1bf17d16 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 13 May 2014 00:06:58 +0200 Subject: [PATCH 18/66] see bug 171, comment #2; introducing a random delay as a workaround for mass-loading of SwissTiming races --- .../domain/swisstimingadapter/impl/SailMasterConnectorImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterConnectorImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterConnectorImpl.java index 7c356bc833f..0b55670faf6 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterConnectorImpl.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterConnectorImpl.java @@ -508,6 +508,8 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement synchronized (ensureSocketIsOpenSemaphor) { while (!stopped && socket == null) { try { + // TODO see bug 171, comment #2; randomized delay to make it unlikely that two requests collide at the M2S server + Thread.sleep((long) (10000 /*ms*/ * Math.random())); // wait somewhere between 0s and 10s. logger.info("Opening socket to " + host + ":" + port + " and sending " + MessageType.OPN.name() + " message..."); socket = new Socket(host, port); From 88215764a57f563a95653840f470890e930c5bd7 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 13 May 2014 01:26:15 +0200 Subject: [PATCH 19/66] use SwissTiming race UUID as RaceDefinition ID --- .../domain/swisstimingadapter/impl/DomainFactoryImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java index f127a475783..d1208426348 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java @@ -191,7 +191,7 @@ public class DomainFactoryImpl implements DomainFactory { Iterable competitors = createCompetitorList(startList, race.getRaceID(), race.getBoatClass()); logger.info("Creating RaceDefinitionImpl for race "+race.getRaceID()); BoatClass boatClass = race.getBoatClass() != null ? race.getBoatClass() : getRaceTypeFromRaceID(race.getRaceID()).getBoatClass(); - RaceDefinition result = new RaceDefinitionImpl(race.getRaceName(), domainCourse, boatClass, competitors); + RaceDefinition result = new RaceDefinitionImpl(race.getRaceName(), domainCourse, boatClass, competitors, race.getRaceID()); regatta.addRace(result); return result; } From c1f439707fd8310dfcb586479eec50bd114f6155 Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Tue, 13 May 2014 09:06:01 +0200 Subject: [PATCH 20/66] Added translation to russian --- .../org.eclipse.core.resources.prefs | 6 +- .../ui/client/StringMessages_ru.properties | 958 ++++++++++++++++++ 2 files changed, 961 insertions(+), 3 deletions(-) create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties diff --git a/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs b/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs index 3d1e6b08933..548f1163d5a 100755 --- a/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs +++ b/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs @@ -1,3 +1,3 @@ -#Fri Jan 13 22:25:34 CET 2012 -eclipse.preferences.version=1 -encoding//src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties=UTF-8 +eclipse.preferences.version=1 +encoding//src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties=UTF-8 +encoding//src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties=UTF-8 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties new file mode 100755 index 00000000000..24f13cd0ac3 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -0,0 +1,958 @@ +helloWorld=Привет Мир +kielWeel2011=Кильская Неделя 2011 +stgAccount=Счет STG +tracTracEvents=События Трак Трак +swissTimingEvents=События Swiss Timing +trackedBefore=Ранее Отслеженные +listRaces=Перечень Гонок +wind=Ветер +windPanelLabel=Это окно информации о ветре, которое до сих пор абсолютно пустое. +refresh=Обновить +remove=Удалить +windSource=Источники информации о ветре +dampeningInterval=Интервал ослабления +map=Карта +race=Гонка +races=Гонки +tracked=Отслежена +time=Время +timeTooltip=Учет времени соперника, выступавшего на этом этапе. На первом этапе учет времени ведется с начала гонки, а не при пересечении соперником линии старта. +playSpeed=Скорость воспроизведения +playSpeedHelp=Используйте "1" для обычной скорости воспроизведения, "2" для удвоенной скорости воспроизведения и т. д. +playModeLive=В прямом эфире +playModeReplay=Режим повтора +timeDelay=Задержка +timeDelayHelp=Не указывайте, для воспроизведения повтора в любой момент времени; введите секунды (например, "60" для одной минуты или "0" без указания) задержки перед загрузкой данных в режиме реального времени +speedInKnots=Скорость (узлы) +fromDeg=Начиная с (градус) +noRacesYet=Гонок пока нет. +total=\u03A3 +carry=Суммарная +competitor=Соперник +competitors=Соперники +defaultLeaderboard=Таблица лидеров по умолчанию +noSuchLeaderboard=Такая таблица лидеров отсутствует +leaderboardConfiguration=Форма таблицы лидеров +newDotDotDot=Новый... +editScores=Изменить баллы... +columnNamesInSelectedLeaderboard=Названия гонок в выбранной таблице лидеров +addDotDotDot=Добавить... +renameDotDotDot=Переименовать... +trackedRaceConnectedToSelectedRaceName=Отслеженная гонка, соединенная с выбранным названием гонки +linkToColumn=Соединить со столбцом +unlink=Отсоединить +leaderboardName=Имя таблицы лидеров +cancel=Отмена +pleaseEnterAName=Пожалуйста, введите имя +pleaseEnterABoatClass=Пожалуйста, введите класс судна +discardRacesFromHowManyStartedRacesOn=Не учитывать еще одну начинающуюся гонку в количестве начавшихся гонок +leaderboardWithThisNameAlreadyExists=Таблица лидеров с таким именем уже существует +discardThresholdsMustBeNumeric=Предельные значения не учитываемых должны быть выражены числом +discardThresholdsMustBeAscending=Предельные значения не учитываемых должны быть возрастающими +discarding=Неучет +startingFromNumberOfRaces=Начиная с количества гонок +renameLeaderboard=Переименовать таблицу лидеров +addColumnToLeaderboard=Добавить столбец в таблицу лидеров +pleaseEnterNameForNewRaceColumn=Введите, пожалуйста, имя для столбца новой гонки +ok=OK +medalRace=Медальная гонка +renameRace=Переименовать гонку +openSelectedLeaderboard=Открыть выбранную таблицу лидеров +rank=Занимаемое место +rankTooltip=Место, занимаемое участником в гонке +regattaRank=Место, занимаемое в регате +regattaRankTooltip=Место, занимаемое участником в регате +overallRank=Итоговое место +overallRankTooltip=Место, занимаемое участником на этапах +totalRegattaRank=Итоговое место в регате +collapse=- +expand=+ +leg=L +distanceInMeters=Расстояние +averageSpeedInKnots=\u2205 Скорость +averageSpeedInKnotsTooltip=Средняя скорость относительно земли в ходе этого этапа,\nрассчитывается путем деления пройденного расстояния на затраченное на это время. +raceAverageSpeedInKnotsTooltip=Средняя скорость относительно земли в ходе этой гонки,\nрассчитывается путем деления пройденного расстояния на затраченное на это время. +rankGain=Улучшение места +rankGainTooltip=Разность между местом, занятым на текущем и предыдущем этапах. +sapSailingAnalytics=Аналитическая информация парусного спорта +leaderboard=Таблица лидеров +leaderboards=Таблицы лидеров +leaderboardSettings=Параметры таблицы лидеров +settings=Параметры +selectAtLeastOneLegDetail=Выберите хотя бы одну подробность об этапе +currentSpeedOverGroundInKnots=Скорость относительно земли (SOG) +currentSpeedOverGroundInKnotsTooltip=Текущая скорость относительно земли. +estimatedTimeToNextWaypointInSeconds=Расчетное время прибытия (ETA). +estimatedTimeToNextWaypointInSecondsTooltip= Расчетное время, необходимое участнику для прибытия в следующую промежуточную точку маршрута, исходя из скорости лавирования участника и с учетом истинного направление ветра. +gapToLeaderInSeconds=Отставание от лидера +gapToLeaderInSecondsTooltip=Время, необходимое участнику для достижения текущего положения лидера, исходя из скорости лавирования. +velocityMadeGoodInKnots=Скорость лавирования (VMG) +velocityMadeGoodInKnotsTooltip=Скорость лавирования, представляет собой скорость продвижения на ветер/ по ветру для этапов движения против ветра/ по ветру соответственно, или скорость в направлении движения при лавировании по ветру +windwardDistanceToGoInMeters=Расстояние, которое необходимо пройти на ветер +windwardDistanceToGoInMetersTooltip=Расстояние движения на ветер/ по ветру от текущего положения участника до конца этапа для этапов движения против ветра/ по ветру соответственно, или скорость в направлении движения при лавировании по ветру. +windwardDistanceToLeaderInMeters=Расстояние на ветер до лидера +windwardDistanceToLeaderInMetersTooltip=Расстояние на ветер/ под ветер от участника до лидера для этапов движения против ветра/ по ветру соответственно или скорость в направлении движения при лавировании по ветру. +name=Имя +leaveEmptyForDefault=Оставьте пустым по умолчанию. +trackNewEvent=Отследить новое событие +trackableRaces=Отслеживаемые гонки +trackWind=Отслеживать ветер +regattaName=Название регаты +eventName=Название события +event=Событие +startTime=Время старта +regatta=Регата +regattas=Регаты +startTracking=Начать отслеживание +stopTracking=Остановить отслеживание +connections=Подключения +historyOfConnections=История подключений +defineNewConnection=Определить новое подключение +trackedRaces=Отслеженные гонки +canSendRequests=Можно отправить запрос +connectAndReadRaces=Подключиться и прочитать данные о гонке +ports=Порты +liveData=Данные в реальном времени +storedData=Сохраненные данные +uris=URI-адреса +jsonUrl=JSON URL +livePort=Порт данных в реальном времени +storedPort=Порт сохраненных данных +liveUri=Данные пользователя в реальном времени +storedUri=Сохраненные данные пользователя +eventsConnectedTo=Текущие отслеживаемые события +hostname=Имя узла +declinationCheckbox=Корректировка направления ветра с помощью отклонения +raceStartTrackingColumn=Отслеживание начато +pauseAutomaticRefresh=Приостановка автоматического обновления +autoRefresh=Обновлять автоматически +delayInSeconds=Задержка (и) +delayBetweenAutoAdvances=Задержка между автоматическими обновлениями в секундах: +delayMustBeNonNegative=Значение задержки должно быть больше нуля +chooseUpdateIntervalOfAtLeastOneSecond=Выберите интервал обновления, он должен быть не менее одной секунды +errorSettingWindForRace=Ошибка при настройке параметра ветер для гонки +errorFetchingWindInformationForRace=Ошибка при получении данных о ветре для гонки +errorWhileTryingToSetWindSourceForRace=Ошибка при попытке установить флажок «начинается встречный ветер» +inEvent=В случае +to=к +showEstimatedWind=Показать оценочный ветер +timing=Хронометраж +raceDetailsToShow=Подробные сведения о гонке +overallDetailsToShow=Общие сведения +legDetailsToShow=Сведения об этапе +columnMoveUp=Вверх +columnMoveDown=Вниз +port=Порт +raceStartTimeColumn=Гонка началась +showOnlySelectedCompetitors=Показать только выбранных участников +showSelectedCompetitorsInfo=Показать окно краткого описания только для выбранных участников +tailLength=Длина (с) хвоста +selectedRaces=Какие гонки вы хотели бы отобразить? +lengthInSeconds=Длина (с) +stressTest=Стресс тест +numberOfManeuvers=Маневры +numberOfManeuversTooltip=Количество маневров, выполненных во время этого этапа. +numberOfManeuversInRaceTooltip=Количество маневров, выполненных во время этой гонки. +tacks=Галсы +jibes=Перекладывания паруса +penaltyCircles=Штрафные круги +medalRaceIsNull=Значение медальной гонки недопустимо +configuration=Конфигурация +maneuverTypes=Маневры +chooseChart=Выбрать график +distanceTraveled=Пройденное расстояние +distanceTraveledTooltip=Расстояние, пройденное от старта до финиша этапа\а не до текущего момента времени, если этап не завершен. +raceDistanceTraveledTooltip=Расстояние, пройденное от старта до финиша гонки\а не до текущего момента времени, если гонка не завершена. +velocityMadeGoodLong=Скорость лавирования +speedOverGroundLong=Скорость относительно земли +gapToLeaderLong=Отставание от лидера +pointsToLoad=Баллов для нагрузки +speed=Скорость +distance=Расстояние +in=в +secondsUnit=с +metersUnit=м +close=Закрыть +compareCompetitors=Сравнить участников +description=Описание +sailNumber=Номер на парусе +country=Страна +no3LetterCodes=Не удалось найти 3-х буквенные коды стран МОК. +add=Добавить +delete=Удалить +showCharts=Показать графики +raceWithThisNameAlreadyExists=Гонка с таким именем уже существует. +raceWithThisNameAlreadyExistsInRegatta=Гонка с таким именем уже существует в этой регате. +headUp=Заголовок +bearAway=Поворачивать под ветер +tack=Галс +tackTooltip=Количество галсов совершенных во время этой гонки. +jibe=Перебрасывать парус +jibeTooltip=Количество перебрасываний паруса во время этой гонки. +penaltyCircle=Штрафной круг +penaltyCircleTooltip=Количество штрафных кругов, пройденных в ходе этой гонки. +markPassing=Проход знака +mark=Знак +otherManeuver=Неизвестный маневр +douglasPeuckerPoints=Точки алгоритма Дугласа-Пойкера +filterRacesByName=Сортировать гонки по названию +trackSettings=Отслеживать параметры +actionEdit=Изменить +actionEditCompetitors=Изменить участников +actionEditScores=Изменить баллы +actionOpenBrowser=Открыть в браузере +actionRemove=Удалить +actionEditSeries=Изменить серии +actionRaceEdit=Изменить или переименовать гонку +actionRaceUnlink=Отсоединить гонку +actionRaceRemove=Удалить гонку +actionExportXML=Экспорт в формате XML +filterLeaderboardsByName=Сортировать таблицы лидеров по названию +actions=Действия +islinked=Соединен +yes=Да +no=Нет +hoverOverAPoint=Наведите курсор на точку для просмотра подробных сведений +noSelection=Ничего не выбрано +raceIsKnownToStartUpwind=Гонка начинается с этапа с наветренной стороны +events=События +settingsForComponent=Параметры для {0} +noEventsFound=События не обнаружены +noEventSelected=События не выбраны +noLeaderboardsFound=Талицы лидеров не обнаружены +searchEvents=Поиск событий +location=Местонахождение +from=С +until=По +onlyLiveEvents=Только события в прямом эфире +locationNotAvailable=Местонахождение недоступно +startDateNotAvailable=Дата начала недоступна +startDate=Дата начала +endDate=Дата окончания +unknownManeuver=Неизвестный маневр +tailLengthMustBePositive=Число длины хвоста должно быть больше нуля. +failedToLoadRaceInformation=Сбой при загрузке сведений о гонке: {0} +competitorPassedMarkAtDate={0} прошли {1} в {2} +valueForCompetitorAt={0} в {1}: {2} +reallyRemoveRace=Вы действительно хотите удалить гонку ''{0}''? Примечание: все записи о гонке будут также удалены без возможности восстановления! +showDetails=Показать подробности +hideLeaderboard=Скрыть таблицу лидеров +hideLeaderboards=Скрыть таблицы лидеров +showLeaderboard=Показать таблицу лидеров +hideRaces=Скрыть гонки +showMap=Показать карту +hideMap=Скрыть карту +noLeaderboardSelected=Таблица лидеров не выбрана +leaderboardGroups=Группы таблицы лидеров +filterLeaderboardGroupsByName=Сортировать группы таблицы лидеров по имени +createNewLeaderboardGroup=Создать группу таблицы лидеров +leaderboardGroupConfiguration=Конфигурация группы таблицы лидеров +legs=Этапы +currentLeg=На этапе +currentLegTooltip=Номер этапа, на котором находится участник в данный момент, начиная с 1 +leaderboardGroup=Группа таблицы лидеров +pleaseEnterNonEmptyDescription=Пожалуйста, введите непустое описание +groupWithThisNameAlreadyExists=Группа таблицы лидеров с этим именем уже существует. +detailsOfLeaderboardGroup=Сведения о группе таблицы лидеров +edit=Изменить +save=Сохранить +abort=Отмена +noLeaderboardGroupWithNameFound=Группа таблицы лидеров с именем {0} не обнаружена +overview=Обзор +welcomeToSailingAnalytics=Добро пожаловать в Аналитическую информацию парусного спорта SAP +welcomeToSailingAnalyticsBody=SAP приближает любителей парусного спорта к происходящим на воде событиям как никогда раньше.\ +\ Таблица лидеров в прямом эфире расширяет возможности для любителей, отображая в режиме реального времени место, занимаемое участником, скорости судов, а также победителей с указанием отрыва. Следите за каждой гонкой и благодаря SAP не пропускайте ни одного совершенного маневра.\ +\ Это самый простой способ наблюдать за парусной гонкой в прямом эфире, где бы вы ни находились. +home=Главная +clickAndDragToZoomIn=Щелкните и перетащите область графика для увеличения +windSpeed=Скорость ветра (узлы) +allTimesInUTC=Время указано в универсальном глобальном времени по Гринвичу (UTC) +autoZoomTo=Автоматическое масштабирование до +autoZoomToBoats=Суда +autoZoomToBuoys=Буи +autoZoomToWindSensors=Датчики ветра +autoZoomOff=Выкл +autoZoomToTails=Хвосты +autoZoomSelectedCompetitors=Масштабировать только выбранных участников +bearing=Направление (ветра) +zoom=Масштабирование +stepSizeMustBeGreaterThanNull=Размер шага должен быть больше 0 +stepSizeInSeconds=Размер шага в секундах +refreshInterval=Обновить интервал (ы) +refreshIntervalMustBeGreaterThanXSeconds=Обновление интервала должно быть больше, чем {0} секунд +competitorCharts=Графики участника +selectAtLeastOneCompetitor=Для отображения графиков участников, выберите хотя бы одного участника +directionChange=Изменение направления +speedChange=Изменение скорости +degreesShort=градус +untracked=Неотслеженная +delayForLiveMode=Задержка для режима прямого эфира: +notAvailable=Недоступно +details=Подробности +noGroupSelected=Группа не выбрана +combinedWindSourceTypeName=Комбинированная +courseBasedWindSourceTypeName=Проложенный курс +trackBasedEstimationWindSourceTypeName=Расчетный +expeditionWindSourceTypeName=Измеренный +webWindSourceTypeName=Вручную +raceCommitteeWindSourceTypeName=Гоночный комитет +clickChartToSetTime=Щелкните по графику для установки времени +position=Положение +windSourcesUsed=Использованные источники информации о ветре +errorTryingToUpdateWindSourcesToExclude=Ошибка при попытке обновления источников информации о ветре для исключения для гонки {0}: {1} +feedback=Комментарии +startStopPlaying=Начать/Остановить воспроизведение +backToLive=Вернуться к моменту времени в прямом эфире +slowPlaySpeedDown=Замедлить скорость воспроизведения или даже воспроизводить назад. +speedPlaySpeedUp=Увеличить скорость воспроизведения или снова воспроизводить вперед. +showHideComponent=Показать/ Скрыть {0} +degreesBoatToTheWind=Угол к ветру +racesInLeaderboard=Гонки в таблице лидеров +leaderboardsInGroup=Таблицы лидеров в группе +replication=Дублирование +errorFetchingReplicaData=Ошибка получения данных дублирования: {0} +averageCrossTrackErrorInMeters=\u2205 XTE +averageCrossTrackErrorInMetersTooltip=Среднее боковое уклонение от заданного курса по большой дуге окружности, соединяющей начало и конец этапа +raceAverageCrossTrackErrorInMetersTooltip=Среднее боковое уклонение от заданного курса по большой дуге окружности, соединяющей начало и конец этапа в течение этой гонки. +enterMaster=Имя узла ведущего экземпляра +errorStartingReplication=Ошибка начала повтора с узла {0} / обмена {1}: {2} +helpLines=Вспомогательные линии +startLine=Линия старта +finishLine=Линия финиша +advantageLine=Линия преимущества +courseMiddleLine=Средняя линия курса +messagingPortNumber=Номер порта обмена сообщениями ( по умолчанию 0) +servletPortNumber=Номер порта сервлета +registeredAt=Зарегистрирован в {0} +replicatingFromMaster=Дублирование с основного {0}, порт передачи обмена сообщениями {1}, порт сервлета {2} +showWindSpeedSeries=Показать данные о силе ветра +showWindDirectionSeries=Показать данные о направлении ветра +fleet=Флот +boatClass=Класс судна +setDelayToLive=Установить задержку от прямого эфира +pleaseEnterNonEmptyVenue=Введите, пожалуйста, место проведения соревнования. +pleaseEnterStartAndEndDate=Пожалуйста, введите дату начала и дату окончания. +startDateMustBeforeEndDate=Дата начала должна предшествовать дате окончания. +eventWithThisNameAlreadyExists=Событие с этим именем уже существует. +venue=Место проведения +pleaseSelectABoatClass=Выберите, пожалуйста, класс судна. +regattaWithThisNameAlreadyExists=Регата с этим именем уже существует. +seriesWithThisNameAlreadyExists=Серия с этим именем уже существует. +fleetWithThisNameAlreadyExists=Флот с этим именем уже существует. +fleets=Флоты +series=Серии +splitFleet=Разделить флот +color=Цвет +medalSeries=Медальные серии +noColor=Цвет не указан +pleaseSelectARegatta=Пожалуйста, выберите регату. +addFleet=Добавить флот +doYouReallyWantToRemoveRegatta=Вы действительно хотите удалить регату "{0}"? Это приведет к удалению без возможности восстановления всех таблиц лидеров для этой регаты! +errorLoadingScoreCorrectionProviders=Ошибка при загрузке провайдеров для корректировки очков: {0} +selectResultListToImportFrom=Выберите список результатов для импорта из +of=из +errorObtainingScoreCorrections=Ошибка получения корректировки очков от провайдера {0} и события {1} для класса судна {2} опубликованного {3}: {4} +assignRaceNumbersToRaceColumns=Назначение номеров гонок из официальных результатов столбам гонок таблицы лидеров +importOfficialResults=Импорт официальных результатов... +selectAll=Выбрать все +apply=Применить +successfullyUpdatedScores=Очки обновлены успешно +errorUpdatingScoresForLeaderboard=Ошибка обновления очков для таблицы лидеров {0}: {1} +maneuverTypesToShowWhenCompetitorIsClicked=Типы маневров для отображения при нажатии на участника +simulateWithStartTimeNow=Моделирования с "сейчас" в качестве времени начала +boatClassDoesNotMatchSelectedRegatta=Выбранные гонки содержат классы судов не соответствующие классу судов ''{0}'' выбранной регаты.\\ Вы действительно хотите использовать регату ''{1}''? +regattaExistForSelectedBoatClass=Есть только одна регата для выбранных классов судов.\\ Вы действительно хотите использовать выбор ''ни одной регаты''? +reload=Перезагрузить +addRegatta=Добавить регату... +exchangeName=Заменить имя +course=Курс +courseLayout=Проложенный курс +buoy=Буй +gate=Ворота +buoys=Буи +waypoint=Точка маршрута +showAllCompetitors=Показать всех участников +boatTails=Лодочные хвосты +resultImportUrls=URL-адреса импорта результатов +errorRefreshingResultImportUrlList=Ошибка обновления списка URL-адресов импорта результатов: {0} +successfullyUpdatedResultImportUrls=Список URL-адресов импорта результатов успешно обновлен +errorRemovingResultImportUrls=Ошибка удаления URL-адресов импорта результатов: {0} +pleaseEnterNonEmptyUrl=Пожалуйста, введите URL-адрес +addResultImportUrl=Добавить URL-адрес импорта результатов +errorAddingResultImportUrl=Ошибка добавления URL-адреса импорта результатов: {0} +errorTryingToObtainLeaderboardContents=Ошибка попытки получения содержимого таблицы лидеров: {0} +lastScoreUpdate=Обновление последних очков +warningBrowserUnsupported=Этот веб-сайт не оптимизирован для вашего браузера. Пожалуйста, выберите браузер новой версии, такой как Firefox или Chrome. +newMark=Новый знак +controlPoint=Знак/Ворота +errorUpdatingRaceCourse=Ошибка обновления курса гонки: {0} +successfullyUpdatedCourse=Курс гонки успешно обновлен +selectOneMarkOrTwoMarksForGate=Выберите одну отметку или две отметки для создания ворот +errorTryingToObtainTheMarksOfTheRace=Ошибка попытки получения отметок дистанции гонки: {0} +errorTryingToObtainRaceCourse=Ошибка попытки получения курса гонки: {0} +insertWaypointBeforeSelected=Вставьте ранее выбранную точку маршрута +insertWaypointAfterSelected=Вставьте точку маршрута, выбранную позднее +scoringSchemeLowPointSystem=Низкобальная (линейная) система подсчета очков +scoringSchemeHighPointSystem=Высокобальная система подсчета очков +scoringSchemeHighPointEssOverall=Высокобальная система подсчета, итоговые баллы экстремальной серии ESS +scoringSchemeHighPointLastBreaksTie=Высокобальная система подсчета, тай-брейк на последней гонке +scoringSchemeLowPointWinnerGetsZero=Низкобальная система подсчета очков, победитель получает ноль очков +scoringSystem=Система подсчета очков +createFlexibleLeaderboard=Создание адаптивной таблицы лидеров +createRegattaLeaderboard=Создание таблицы лидеров регаты +editFlexibleLeaderboard=Изменение адаптивной таблицы лидеров +editRegattaLeaderboard=Изменение таблицы лидеров регаты +defaultRegatta=Регата по умолчанию +useOverallLeaderboard=Использование общей таблицы лидеров +overallStandings=Общий зачет +type=Тип +actionAddRaces=Добавить гонки +racesWithNamePrefix=Гонки с именем-префиксом +maximumSpeedOverGroundInKnots=Максимальная скорость относительно земли +maximumSpeedOverGroundInKnotsTooltip=Самая большая скорость относительно земли участника, измеренная в ходе регаты. +totalTimeSailedDownwindInSeconds=Общее время под ветером +totalTimeSailedDownwindInSecondsTooltip=Общее время, которое в течение регаты участник шел под ветер. +totalTimeSailedUpwindInSeconds=Общее время против ветер +totalTimeSailedUpwindInSecondsTooltip=Общее время, которое в течение регаты участник шел против ветер. +totalTimeSailedReachingInSeconds=Общее время лавирования по ветру +totalTimeSailedReachingInSecondsTooltip=Общее время этапов лавирования по ветру участником в ходе регаты. +totalTimeSailedInSeconds=Общее время +totalTimeSailedInSecondsTooltip=Общее время, которое участник проплыл в регате. +totalDistanceTraveled=Общее расстояние +totalDistanceTraveledTooltip=Общее расстояние, пройденное во время регаты. +totalAverageSpeedOverGround=\u2205 Скорость относительно земли +totalAverageSpeedOverGroundTooltip=Средняя скорость относительно земли на протяжении регаты. +hhmmssUnit=чч:мм:сс +actionAddWindData=Добавить данные о ветре +valueMustBeBetweenMinMax=Значение ''{0}''должно быть в пределах от {1} до {2}. +optional=Дополнительно +pleaseEnterAValue=Введите, пожалуйста, значение. +latitude=Широта +longitude=Долгота +actionAddEvent=Добавить событие +publicationUrl=URL-адрес для публикации +suppress=скрыть +isPublic=является открытым +suppress=скрыть +unsuppress=Отобразить +suppressedCompetitors=Скрытые участники +mediaPanel=Аудио и Видео +url=URL +mimeType=MIME-тип +title=Заголовок +duration=Продолжительность (ч:мм:сс.мс) +addMediaTrack=Добавить аудио/видео +addYoutubeTrack=Добавить Youtube +youtubeId=Идентификатор Youtube +reallyRemoveMediaTrack=Вы действительно хотите удалить медиа трек''{0}''? +pleaseSelectASeries=Пожалуйста, выберите серию +pleaseSelectARace=Пожалуйста, выберите гонку +displayGroupsInReverseOrder=Отобразить группы в обратном порядке +actionConfigureUrl=Настроить URL-адрес +configureLeaderboardUrlDescription=Настроить таблицу лидеров и получить результирующий URL-адрес по ссылке +embedded=Внедренный +additionalUrlSettings=Дополнительные параметры URL-адреса +restrictToSingleSelectedRace=Ограничить одной выбранной гонкой +expandSelectedRace=Расширить выбранную гонку +showRaceDetails=Разрешить подробности для гонки +numberOfRaces=Количество гонок +numberOfRacesMustBeNonNegativeNumber=Пожалуйста, введите количество последних гонок, которые должны отображаться +chooseTheWayYouSelectRaces=Выберите способ выбора гонок +selectFromAllRaces=Выбрать из всех гонок +selectANumberOfRaces=Выбрать несколько гонок +numberOfLastNRaces=Количество самых последних гонок для отображения +expandLastRace=Всегда раскрывать последнюю отображенную гонку +factor=Фактор +errorUpdatingIsMedalRace=Ошибка обновления параметров медальной гонки: {0} +maneuverLoss=Потеря при выполнении маневра +averageManeuverLossInMeters=\u2205 Потеря при выполнение маневра +averageManeuverLossInMetersTooltip=Усредненная потеря расстояния при выполнении маневра. +averageTackLossInMeters=\u2205 Потеря при смене галса +averageTackLossInMetersTooltip=Усредненная потеря расстояния при смене галса. +averageJibeLossInMeters=\u2205 Потеря при перекладывании паруса +averageJibeLossInMetersTooltip=Усредненная потеря расстояния при перекладывании паруса. +shape=Форма +pattern=Модель +legend=Легенда +live=прямой эфир +swissTimingArchiveConnector=события SwissTiming, помещенные в архив +errorFetchingChartData=Ошибка получения данных графика: {0} +loadingCompetitorData=Загрузка данных участника... +competitorRegattaDataAfterRaceN={0} после гонки {1} +rankChart=Рейтинг +totalPoints=Итоговое количество очков +raceTotalPointsTooltip=Итоговое количество очков участника в гонке +regattaTotalPointsTooltip=Итоговое количество очков участника в регате +showRankChart=Показать таблицу рейтинга +overallLeaderboardSelection=Итоговая таблица лидеров +errorTryingToObtainOverallLeaderboards=Ошибка получения итоговых таблиц лидеров: {0} +finished=Завершившиеся +passedTo=Переданные в {0} +gapChangeSinceLegStartInSeconds=Изменение отставания +gapChangeSinceLegStartInSecondsTooltip=Изменение отставания от лидера с начала последнего этапа.\nОтрицательное число означает, что отставание уменьшилось;\nположительное число означает, что отставание увеличилось. +sideToWhichMarkAtLegStartWasRounded=Отметка округленная до +sideToWhichMarkAtLegStartWasRoundedTooltip=Сторона в которую округляется отметка на старте этапа. +raceIsLive=Гонка {0} в прямом эфире +racesAreLive=Гонки {0} в прямом эфире +knotsUnit=Узлы +scoringSchemeHighPointFirstGetsOne=Самый лучший результат, победитель получает 1 очко +scoringSchemeHighPointFirstGetsTen=Самый лучший результат, победитель получает 10 очков +knotsUnit=Узлы +generatePolarSheet=Создать полярную электронную таблицу +polars=Полярная диаграмма +polarSheetChart=Полярная электронная таблица +polarSheetMinimumDataSizePerGraph=Минимальное количество данных на графике +polarSheetMinimumDataSizePerGraphTooltip=Все графики с меньшим количеством точек данных визуально не отображаются.\nТаким образом, не отображаются отвлекающие внимание диаграммы для \nскоростей ветра, в которых отсутствует достаточный объем данных. +polarSheetMinimumDataSizePerAngle=Минимальное количество точек данных в расчете на угол +polarSheetMinimumDataSizePerAngleTooltip=На каждом графике будут отображаться только данные об углах с указанным числом\nбазовых точек данных. Не имеющие важного значения\nчасти графика могут быть скрыты с помощью задания этой пороговой величины. +polarSheetMinimumWindConfidence=Минимальное значение достоверных данных о ветре +polarSheetMinimumWindConfidenceTooltip=Только фиксированные значения ветра с достоверностью, превышающей указанное пороговое значение\nприменяются. Эта достоверность от 0 до 1. +polarSheetMinimumConfidenceMeasure=Минимально допустимое измерение достоверности +polarSheetMinimumConfidenceMeasureTooltip=Минимальная степень достоверности для точки, указанной на графике.\nЕсли она ниже установленного порогового значения, то точка не отображается.\nЗначение лежит от 0 до 1 и детерминируется \nс использованием статистических мер. +polarSheetUseOnlyWindGaugeData=Для определения скорости ветра используйте только данные, полученные с помощью анемометра +polarSheetUseOnlyWindGaugeDataTooltip=Благодаря учету данных о скорости ветра, полученных с помощью анемометра\nмы исключаем примерные значения скорости ветра, введенные вручную, которые могли бы исказить данные, отображенные на полярной диаграмме. +polarSheetUseOnlyEstimationData=Используйте, определенные на глаз значения только для направления ветра +polarSheetUseOnlyEstimationDataTooltip=Использование точных расчетных значений о направлении ветра,\nпозволяет нам устранить ошибки полярных диаграмм, которые вращаются из-за неправильных данных, полученных с анемометра\n. +polarSheetRemoveOutliers=Удалите сильно отличающиеся значения (Подход основан на расстоянии) +polarSheetRemoveOutliersTooltip=Сильно отличающиеся значения удаляются с помощью применения подхода, основанного на расстоянии.\nПараметры могут быть отрегулированы ниже. +polarSheetOutlierDetectionRadius=Соседний радиус, определяющий сильно отличающееся значение +polarSheetOutlierDetectionRadiusTooltip=Радиус, используемый в алгоритме определения сильно отличающегося значения\nна основании расстояния. +polarSheetOutlierDetectionMinimumPerc=Минимальный процент соседних точек, определяющий сильно отличающееся значение +polarSheetOutlierDetectionMinimumPercTooltip=Процент соседних точек данных /всего точек данных\nдолжен быть ниже указанного значения для того, чтобы считаться сильно отличающимся значением. +polarSheetNumberOfHistogramColumns=Количество столбцов гистограммы +polarSheetWindSteppingInKnots=Шаг значения ветра в узлах +polarSheetWindSteppingMaxDistance=Максимальное расстояние до уровня ветра +polarSheetWindSteppingMaxDistanceTooltip=Устанавливает максимальный радиус, для которого уровни ветра будут фиксированными. +polarSheetSplitByWindGauges=Разделить источники данных о ветре +polarSheetSplitByWindGaugesTooltip=Разделение источников данных о ветре позволяет найти неисправные анемометры.\nСтатистическая правильность не гарантируется. Такое исправление может \nбыть учтено в полярной диаграмме результатов несколько раз (для разных анемомтеров). +pleaseSeeToolTips=Пожалуйста, наведите курсор на свойства, чтобы ознакомиться с всплывающими подсказками, при их наличии. +competitorColumnTooltip=Имена членов команды этого участника. +sailIdColumnTooltip=Отображает флаг страны и номер на парусе участника. +rankColumnTooltip=Итоговое место этого участника в регате. +carryColumnTooltip=Очки, перенесенные из предыдущих гонок. +totalsColumnTooltip=Итоговое количество очков участника в регате. +windData=Данные о ветре +gpsData=GPS-данные +status=Статус +noDataFound=Данные не обнаружены +displayName=Отобразить имя +histogram=Гистограмма +numberOfDataPoints=Количество точек данных +angleAndTotalNumberOfDataPointsAndCovAndCm=Угол: {0}; Общее количество точек данных: {1}; Коэффициент отклонения: {2}; Степень достоверности: {3} +buoyZone=Зона буя +radiusInMeters=Радиус (м) +filterRegattasByName=Сортировка регат по имени +noRegattasYet=Пока регат нет. +addCourseArea=Добавить зону курса +courseAreas=Зоны курса +courseArea=Зона курса +pleaseEnterNonEmptyCourseArea=Пожалуйста, выведите зону курса. +pleaseSelectACourseArea=Пожалуйста, выберите зону курса. +radiusInMeters=Радиус (м) +lastUpperFlag=Верхний флажок +lastLowerFlag=Нижний флажок +cannotAddRacesToRegattaLeaderboardButOnlyToRegatta=Это таблица лидеров регаты. Вы можете добавлять гонки в эту таблицу лидеров только непосредственно в определении регаты. +correctScore=Исправить очки +correctScoreFor=Исправить очки для участника {0} в гонке {1} +penaltyOrRedress=Штраф/Корректировка +netScore=Чистые очки +timePointMustBeAfterStartOfTracking=Момент времени должен быть после начала отслеживания. +timePointMustBeBeforeEndOfTracking=Момент времени должен быть до окончания отслеживания. +regattaUsedForTheTrackedRace=Регата, используемая для отслеживаемой гонки +tracTracUpdateUrl=Обновление URl-адреса сервера ТракТрак +passingInstructions=Инструкции по передаче +refreshNow=Обновить сейчас +stopUpdating=Остановить обновление +startUpdating=Начать обновление +currentTime=Текущее время: +tractracUsername=Имя пользователя ТракТрак +tractracPassword=Пароль ТракТрак +operatorEquals=Равно +operatorNotEqualTo=Не равно +operatorLessThan=Меньше +operatorLessThanEquals=Меньше или равно +operatorGreaterThan=Больше чем +operatorGreaterThanEquals=Больше или равно +operatorContains=Содержит +operatorNotContains=Не содержит +operatorStartsWith=Начинается с +operatorEndsWith=Заканчивается на +nationality=Гражданство +numberMustBePositive=Цифра должна быть больше нуля. +distanceToLineAtRaceStart=Расстояние до линии «Старт» +distanceToLineFiveSecondsBeforeStart=Расстояние до линии 5с до старта +speedOverGroundAtRaceStart=Скорость относительно земли на линии «Старт» +speedOverGroundFiveSecondsBeforeStart=Скорость относительно земли на линии 5с до старта +speedOverGroundWhenPassingStart=Скорость относительно земли во время старта +distanceToLineAtRaceStartTooltip=Расстояние до стартовой линии во время начала гонки +distanceToLineFiveSecondsBeforeStartTooltip=Расстояние до стартовой линии за пять секунд до начала гонки +speedOverGroundAtRaceStartTooltip=Скорость относительно земли во время начала гонки +speedOverGroundFiveSecondsBeforeStartTooltip=Скорость относительно земли за пять секунд до начала гонки +speedOverGroundWhenPassingStartTooltip=Скорость относительно земли при пересечении участником линии старта +startTack=Сменить галс +startTackTooltip=галс, на котором участник начал +portTack=Левый галс +starboardTack=Правый галс +portSide=Левый борт +starboardSide=Правый борт +pleaseEnterANumber=Пожалуйста, введите номер. +competitorsFilter=Сортировать участников +flagStatus=Флажок состояния +additionalInformation=Информация +windFixListingDescription=Исправления для ветра (первые 3, последние 3, фактические значения) +noWindFixesAvailable=Исправления для ветра недоступны. +distanceToStarboardEndOfStartlineWhenPassingStart=Расстояние от линии до правого борта во время прохождения старта +distanceToStarboardEndOfStartlineWhenPassingStartTooltip=Расстояние до правого борта от стартовой линии или одной отметки начала, когда участник пересек линию старта +actionAddFilter=Добавить фильтр +actionEditFilter=Изменить фильтр +availableFilters=Доступные фильтры +createFilterHint=Создать набор фильтров для сортировки списка участников. +filterThisNameAlreadyExists=Фильтр с таким именем уже существует. +addAtLeastOneFilterCriteria=Добавьте хотя бы один критерий сортировки. +filterCriteria=Критерий сортировки +selectAFilterCriteria=Выбрать критерий сортировки +filterName=Имя фильтра +operator=Оператор +value=Значение +nationalityMustBeISOorIOCcode=Гражданство должно содержать двухбуквенный код МОК или трехбуквенный код МОК. +raceRank=Рейтинг гонки +waypoints=Точки маршрута +disableRaceFilter=Отключить фильтр +enableRaceFilter=Включить фильтр +raceStatusColumn=Статус гонки +loading=Загрузка (при медленном соединении с Интернетом процесс может занять несколько минут)... +racesWithHiddenState=Гонки со статусом «СКРЫТЫ» +mediaDateFormatError=Недействительный формат даты. Использовать: {0} +selectedCompetitors=Выбранные участники +filterNothing=Нечего сортировать +topNCompetitorsByRaceRank=Лидеры гонки {0} +topNCompetitorsByTotalRank=Лидеры в общем зачете {0} +simulator=Модель +optionsBar=Панель опций +simulateButton=Прогнозировать +setup=Установка +strength=Сила (в узлах) +oscillation=Колебание +oscillating=Колеблющийся +amplitude=Амплитуда(в градусах) +frequency=Частота (в 1/ч) +noise=Шум +sailing=Плавание под парусом +optimality=Оптимальность +maximumTurns=Максимальное количество галсов +raceDirection=Направление гонки +upWind=Против ветра +downWind=По ветру +strategies=Стратегии +omniscient=Всезнающий +opportunistic=Оппортунист +mixed=Смешанный +maps=Карты +startEnd=Старт-Финиш +racecourse=Курс +summary=Сводная информация +replay=Повтор +display=Отобразить +simulatorPolarHeader = Полярная диаграмма +show=Показать +hide=скрыть +leftWind=Ветер слева +rightWind=Ветер справа +strategyOption1=стратегияВариант1 +strategyOption2=стратегияВариант2 +strategyOption3=стратегияВариант3 +legLabel=Этап +raceLabel=Гонка +competitorLabel=Участник +freemode=Сохранять направления +whatIfCourse=Что если курс +editRegatta=Изменить регату... +selectSailingEvent=Пожалуйста, выберите событие парусного спорта... +selectCourseArea=Пожалуйста, выберите зону курса.. +courseAreaOverview=Обзор зоны +courseDesignOverview=Обзор создания курса +regattaOverview=Обзор регаты +additionalSettings=Дополнительные параметры +showFollowingCourseAreas=Показать следующие зоны курса +showFollowingRegattas=Показать следующие регаты +showOnlyRacesOfSameDay=Показать только гонки, проходившие в один и тот же день +showOnlyCurrentlyRunningRaces=Показать только проходящие гонки +seriesDefinesResultDiscardingRule=Серии определяют правила исключения результатов +addSeries=Все серии +regattaDefinesResultDiscardingRules=Регата определяет правила исключения результатов +startsWithZeroScore=Серия начинается с нуля очков +masterDataImportPanel=Импорт основных данных +importSuccess=Успех\nСозданная {0} группа таблиц (ы) лидеров и {1} таблиц (ы) и {2} события (й) и {3} регат (ы). +importSuccessOverwriteInfo=Пришлось перезаписать следующие регаты, необходимо повторно проложить их гонки: +regattaOverviewConfiguration=Обзор конфигурации регаты +firstRaceIsNonDiscardableCarryForward=Начиная с не исключаемых переносов в другую графу +addReplicationMaster=Подключение к главному серверу дублирования +connect=Подключиться +connectToMaster=Подключиться к Главному +exchangeHost=Имя узла обмена сообщениями +explainExchangeHostName=Имя узла, где сообщения ставятся в очередь ожидания. +explainReplicationHostname=Имя узла, где работает главный сервер дублирования. +explainReplicationExchangeName=Имя очереди ожидания обмена сообщениями. Она должна быть настроена как главный сервер. +explainReplicationExchangePort=Порт очереди ожидания обмена сообщениями. Он используется после первоначальной загрузки для всех данных. +explainReplicationServletPort=Номер порта сервлета, используемого для подключения для регистрации этой копии и получения первоначальной загрузки. +explainReplicasRegistered=Копии зарегистрированы (этот сервер главный ): +explainConnectionsToMaster=Подключения к главному серверу (это сервер копий): +explainNoConnectionsToMaster=Не настроенных подключений к главному серверу.Для настройки одного соединения щелкните «Подключиться к главному серверу» . +explainNoConnectionsFromReplicas=Подключений к серверу копий не обнаружено. Копии можно подключить используя их Консоль администрирования. +stopConnectionToMaster=Остановить копирование +asLink=Как ссылка +noStarttimeAnnouncedYet=Ожидание +raceIsRunning=Проходящая +raceIsFinishing=Завершающаяся +raceIsFinished=Завершенная +raceIsScheduled=Запланированная +raceIsInStartphase=Этап старта +generalRecall=Общая отмена +startPostponed=Отложенная +raceAbandoned=Прекращенная +stopAllReplicas=Разъединить соединение со всеми копиями (Опасно!) +warningServerIsReplica=ВНИМАНИЕ: Этот сервер настроек может быть сервером копий. НЕ ВНОСИТЕ никаких изменений или это приведет к нежелательным последствиям! +dropReplicaConnection=Разъединить соединение +timePanelName=Контроль времени +competitorFilterExplanation=Фильтр отображает всех участников, соответствующих критериям: +dropReplicaConnection=Разъединить соединения +protestTimeFinishesAt=Срок подачи протеста истекает в +protestTimeFinishedAt=Срок подачи протеста истек в +flags=Флаги +lastUpdate=Последние обновления +noMoreRacingToday=Сегодня больше нет гонок +furtherSignalsAshore=Дальнейшие сигналы на берегу +earlyStarters=Совершивших фальшстар +showingDetailsOfRace=Отобразить подробности для гонки {0} +startAt=Старт в +finishAt=Финиш в +protestEndsAt=Срок подачи протеста завершается в +unknown=Неизвестно +earlyStarters=Совершивших фальшстарт +raceLog=Запись о гонке +refreshRaceLog=Обновить запись о гонке +raceLogReloaded=Запись о гонке успешно повторно загружена из базы данных +reloadAllRaceLogs=Повторно загрузить все записи о гонке +export=Эскпорт +sideline=Боковая линия +classAndGolfUp=Флаг поднят – восемь минут до старта – процедура старта – через стартовые ворота +classDisplayed=Флаг поднят – пять минут до старта +papaDisplayed=Флаг режима старта четыре минуты до старта +blackDisplayed=Флаг режима старта – дисквалификация совершивших фальшстарт – четыре минуты до старта +zuluIndiaDisplayed=Флаг режима старта – штрафы для совершивших фальшстарт – четыре минуты до старта +papaRemoved=Флаг режима старта - одна минута до старта +blackRemoved=Флаг режима старта – дисквалификация для совершивших фальшстарт - одна минута до старта +zuluIndiaRemoved=Флаг режима старта – старт со штрафами совершившим фальшстарт – одна минута до старта +classFlagRemoved=Флаг убран – сигнал начала гонки +xray=Индивидуальная отмена – есть совершившие фальшстарт +firstSubstitute=Общая отмена – слишком много совершивших фальшстарт – процедура старта будет повторена +blueFlagDisplayed=Флаг отображается – первый участник пересек линию финиша +blueFlagRemoved=Флаг убран – последний участник пересек линию финиша или предельное время финиша прошло +golfRemoved=Флаг убран –ворота закрыты +answeringPennantOverAlphaDisplayed=Флаг отображается - старт отложен - сегодня больше нет гонок +answeringPennantOverHotelDisplayed=Флаг отображается – старт отложен - дальнейшие сигналы на берегу +answeringPennantDisplayed=Флаг отображается – старт отложен +answeringPennantRemoved=Флаг убран – процедура старта начинается через одну минуту +novemberOverAlphaDisplayed=Флаг поднят - гонка отменена – сегодня гонок больше нет +novemberOverHotelDisplayed=Флаг поднят – гонка отменена - дальнейшие сигналы на берегу +novemberDisplayed=Флаг поднят – гонка отменена +bravoDisplayed=Срок подачи протеста начался +bravoRemoved=Срок подачи протеста закончился +essThreeDisplayed=Три минуты до старта +essTwoDisplayed=Две минуты до старта +essOneDisplayed=Одна минуты до старта +essOneRemoved=Сигнал начала гонки +dataBeforeAfter=Данные до/после гонки +rawFixes=Все значения (включая сильно отличающиеся значения) +importRemoteHost=Удаленный узел: +importFetchRemoteLgs=Получить список группы таблицы лидеров +importUrlInvalid=Недействительный URL-адрес для получения групп таблицы лидеров: {0} +importGetLeaderboardsFailed=Запрос ПОЛУЧИТЬ группы таблиц лидеров выполнить не удалось для узла {0} с ошибкой {1} +importNoDataReturned=Данные сервером не возвращены +importServerError=Запрос ПОЛУЧИТЬ группы таблиц лидеров выполнить не удалось с ошибкой Сервера. Дополнительные сведения, см. в журнале сервера. +importLeaderboardGroups=Группы таблиц лидеров: +importOverrideSwitchLabel=Перезаписать существующие данные, если имена и идентификационные данные совпадают +importSelectedLeaderboardGroups=Импорт выбранных групп таблиц лидеров +importSelectAtLeastOne=Нужно выбрать хотя бы одну группу таблиц лидеров для импорта +deselectAll=Отменить выбор всех +polarDiagramButton=Полярная диаграмма +ratioBetweenTimeSinceLastPositionFixAndAverageSamplingInterval=Запаздывание GPS +ratioBetweenTimeSinceLastPositionFixAndAverageSamplingIntervalTooltip=Соотношение между последними полученными с помощью GPS данными о положении и усредненным интервалом выборки +trackingQuality=Качество отслеживания +legType=Тип этапа +sailID=Номер на парусе +seriesLeaderboard=Серия таблиц лидеров +regattaLeaderboards=Таблица лидеров регаты +clearSelection=Отменить выбор +running=Проходящая в настоящее время +runAsSubstantive=Начать +done=Готово +lastFinished=Последняя завершенная +run=Начать +times=раз +numberOfGPSFixes=Количество точек фиксации GPS +averageCleanedServerTime=\u2205 Чистое время на сервере +averageCleanedOverallTime=\u2205 Чистое итоговое время +dataMiningBenchmarkResults=Результаты сравнительного теста сбора данных +serverTime=Время на сервере +confidenceShouldBeBetween=Достоверность данных должна быть от 0 до 1. +minDataValuesNeedToBeAtLeastZero=Минимальные значения объема данных не могут быть отрицательными. +numberOfColumnsAtLeast2=Количество столбцов гистограммы должно быть больше одного. +outlierRadiusNeedsToBePositiveString=Сильно отличающееся значение радиуса должно быть положительным числом. +outlierMinimumNeighborhoodPctString=Сильно отличающееся значение в процентах соседней точки должно быть положительным и максимально равно 1. +oneOrMoreParametersAreEmpty=Поле параметра не может быть пустым. +errorWhileAddingSeriesToChart=При добавлении \nновой серии в график возникла ошибка +generatingPolarSheet=Создание полярной таблицы... +dataCount=Объем данных +generationFinished=Создание завершено! +generating=Создание +removeAllSheets=Удалить все листы +removeAll=Удалить все +selectSheet=Выбрать лист +cleanedServerTime=Чистое время на сервере +overallTime=Общее время +cleanedOverallTime=Чистое итоговое время +dataMiningResult=Результат сбора данных +groupBy=Группировать по +statisticToCalculate=Рассчитать статистику +queryResultsChartSubtitle=Обработанные {1}/{0} элементы данных через {2} секунд +noQuerySelected=Запросы не выбраны +runAutomatically=Запустить автоматически +windImport_Upload=Загрузить +windImport_Title=Импортировать данные о ветре от экспедиции +windImport_BoatId=Идентификационный номер судна: +windImport_SelectedRacesWarning=Вы действительно хотите импортировать данные о ветре в выбранные {0} гонки?\nЭто в любом случае повлияет только на гонки с применимым временным окном. +windImport_AllRacesWarning=Вы действительно хотите импортировать данные о ветре во все гонки?\nЭто в любом случае повлияет только на гонки с применимым временным окном. +windImport_ResultHeader=Импортированные данные с: {0} по: {1}. {2} повлияли на гонки. +windImport_ResultEntry=Гонка: {0} ({1}) - {2} корректировка с: {3} по: {4} +windImport_ResultError=Ошибка: {0} +showWindSpeedSeriesTooltip=Переключает видимость скорости ветра для всех источников данных +showWindDirectionSeriesTooltip=Переключает видимость направления ветра для всех источников данных +combinedWindSourceTypeTooltip=Объединяет данные из всех других источников данных о ветре в один набор данных +courseBasedWindSourceTypeTooltip=Значение основано на прокладке курса гонки (с учетом предположения, что гонка начнется с этапа против ветра) +trackBasedEstimationWindSourceTypeTooltip=Расчетные данные из маршрута участника +expeditionWindSourceTypeTooltip=Данные, полученные из мобильных систем измерения +webWindSourceTypeTooltip=Значения данных, установленные вручную +raceCommitteeWindSourceTypeTooltip=Значения данных, установленные вручную, полученные от официальных лиц гонки +sheetName=Имя листа +sheetNameTooltip=Вы можете присвоить имя своему полярному листу используя текстовое поле.\nИмя будет создано с использованием класса судна\nесли имя не предоставлено. +timeDownwind=Время по ветру +timeReaching=Время лавирования по ветру +timeUpwind=Время против ветра +raceTimeTooltip=Общее время затраченное в этой гонке +raceTimeDownwindTooltip=Общее время движения по ветру в этой гонке +raceTimeReachingTooltip=Общее время лавирования по ветру в этой гонке +raceTimeUpwindTooltip=Общее время движения против ветра в этой гонке +noStatisticSelectedError=Статистических данных для расчета не выбрано +noCustomGrouperScriptTextError=Скрипт блока группирования пустой +noDimensionToGroupBySelectedError=Размер для группировки по не выбран +noGrouperSelectedError=Тип блока группирования не выбран +queryNotValidBecause=Запрос не возможен, т.к. +dataMining=Сбор данных +errorRunningDataMiningQuery=Ошибка при выполнении запроса +hideToolbar=Скрыть панель инструментов +showSeriesLeaderboards=Показать таблицу лидеров серии +showOverallLeaderboard=Показать общую таблицу лидеров +lineAngleToWindAndAdvantage=длина: {0}м; угол к ветру: {1}градус; {2} борт {3}м имеет преимущество +doYouReallyWantToRemoveEvents=Вы действительно хотите удалить все события? +doYouReallyWantToRemoveEvent=Вы действительно хотите удалить событие {0}? +line=Строка +offset=Смещение +doYouReallyWantToRemoveCompetitors=Вы действительно хотите удалить всех выбранных участников? +doYouReallyWantToRemoveCompetitor=Вы действительно хотите удалить выбранного участника {0}? +filterCompetitors=Сортировать по имени, классу судна или номеру на парусе +editCompetitor=Изменить участника +pleaseEnterASailNumber=Пожалуйста, введите номер на парусе +successfullyAllowedCompetitorReset=Перезагрузка участника (ов) {0} разрешена. +id=Идентификатор +allowReload=Разрешить перезагрузку +compress=Сжать +compressTooltip=Используйте только, если на экземпляре сервера, с которого выполняется экспорт, хотя бы\nзапущена операция подтверждения 0fbf6071dea125bec4a56dee55d61c99def4a62e. +queryRunner=Отправка запросов +rerunQueryAfterRefresh=Повторно выполнить запрос после обновления +refreshIntervalMustntBeEmpty=Интервал обновления не может быть пустым +selectionTables=Выбор таблиц +addConfiguration=Добавить конфигурацию +noConfigurations=Конфигурации отсутствуют. +errorRetrievingConfiguration=Ошибка получения данных о конфигурации с сервера. +allowedCourseAreas=Разрешить зоны курса +minimumRoundsForCourse=Минимальное количество кругов для курса гонки +maximumRoundsForCourse=Максимальное количество кругов для курса гонки +resultsMailRecipient=Результаты получателя почты +deviceConfiguration=Конфигурация устройства +none=Ни одного +courseDesignerMode=Создатель курсов +racingProcedure=Процедура гонки +courseNames=Имена курсов гонок +generate=Создать +dontoverwrite=Не перезаписывать +setRacingProcedureConfiguration=Настроить конфигурацию регаты +racingProcedureConfiguration=Конфигурация регаты +activateIndividualRecall=Активировать индивидуальную отмену +rrs26Start=RRS26 старт +setConfiguration=Активировать конфигурацию +classFlag=Флаг класса +classFlagHelpText=Устанавливает флаг класса, используемый для {0}. Выберите ''Не перезаписывать''для активации флага по умолчанию для процедуры. +individualRecallHelpText=После активации можно обратиться к официальному лицу гонки для индивидуальной отмены. +startmodeFlags=Флаги режима старта +startmodeFlagsHelpText=Устанавливает доступные флаги для режима старта. Выберите «ни один» из предложенных, для включения флагов по умолчанию для этой процедуры. +gateStart=Ворота старта +activatePathfinder=Активировать «Следопыт» +pathfinderHelpText=После активации официальное лицо гонки может настроить судно «Следопыт». +essStart=Старт ESS +basicStart=Основной старт +configured=Настроенный +overwriteRacingProceduresConfiguration=Активировать конфигурацию регаты +overwriteRacingProceduresConfigurationHelpText=Включение этой опции предоставляет базовую конфигурацию регаты для всех опций, не настроенных в специальной конфигурации регаты. +setStartTime=Установить время старта +currentPass=Текущий пропуск +setStartTimeDescription=Выбрать новое время старта для гонки +hasAdditionalGolfDownTime=Активировать дополнительное время отсчета +additionalGolfDownTimeHelpText=Активировать дополнительное время отсчета +invalidColor=Недействительный цвет: {0} +igtimiAccounts=Учетные записи Igtimi +errorFetchingIgtimiAccounts=Ошибка получения всех Igtimi учетных записей: {0} +addIgtimiAccount=Добавить учетную запись Igtimi +errorAuthorizingAccessToIgtimiUser=Ошибка авторизации доступа к Igtimi пользователю {0}: {1} +successfullyAuthorizedAccessToIgtimiUser=Успешная авторизация доступа к Igtimi пользователю {0} +couldNotAuthorizedAccessToIgtimiUser=Невозможно авторизовать пользователя {0}; недействительное имя пользователя или пароль +errorGettingIgtimiAuthorizationUrl=Ошибка получения URL-адреса для авторизации Igtimi: {0} +addIgtimiUser=Добавить пользователя Igtimi +doYouReallyWantToRemoveLeaderboards=Вы действительно хотите удалить таблицы лидеров? +errorTryingToRemoveIgtimiAccount=Ошибка попытки удаления учетной записи Igtimi для пользователя {0} +successfullyRemoveIgtimiAccount=Учетная запись Igtimi для пользователя {0} удалена успешно +eMailMustNotBeEmpty=Адрес электронной почты не должен быть пустым +emailAddress=адрес электронной почты +password=Пароль +doYouReallyWantToRemoveLeaderboard=Вы действительно хотите удалить таблицу лидеров: ''{0}''? +metaLeaderboardCannotBeChanged=Это мета таблица лидеров. Ее нельзя изменить здесь. +failedToSetNewStartTime=Ошибка при установке нового времени старта. +doYouReallyWantToRemoveIgtimiAccount=Вы действительно хотите удалить учетную запись Igtimi, предоставляющую доступ к данным пользователя {0}? +igtimiWindImport=Импорт данных о ветре из Igtimi +seeIgtimiTabForAccountSettings=См. вкладку ''Igtimi'' для параметров учетной записи, определяющих какие данные из Igtimi будут учитываться. +importWindFromIgtimi=Импорт данных о ветре из Igtimi +errorImportingIgtimiWind=Ошибка импорта данных о ветре из Igtimi: {0} +resultFromIgtimiWindImport=Следующее количество фиксированных точек было импортировано для перечисленных гонок: {0} +timeToStart={0} до старта +timeSinceStart={0} со старта +distanceToLine=Растояние до линии старта +beatAngleTooltip=Угол между направлением движения участника и ветром +beatAngle=Угол лавирования +runAutomaticallyTooltip=Запустить запрос автоматически, например, после изменения статистических данных или группировки. +rerunQueryAfterRefreshTooltip=Повторно запускает запрос после обновления таблиц. +queryDefinitionProvider=Поставщик определения запроса +statisticProvider=Поставщик статистических данных +toCalculateThe=для расчета +use=Использовать +groupingProvider=Поставщик группировки +releaseNotes=Новости и история выпусков +hasSplitFleetContiguousScoring=Разделить флоты, имеющие близкие количества очков +overallProgress=Итоговое улучшение +eventOverview=Обзор события +start=Старт +showAddedScores=Показать добавленные очки для каждой записи таблицы лидеров +ratio=коэффициент +importWind=Имопрт данных о ветре +importWindTooltip=Снятие флажка с этой опции значительно снизит объем передаваемых данных. +authorName=Имя автора +authorPriority=Приоритет автора From 4361c951e62f68d4c6276a2bbd32bbcc3f62aa7b Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Tue, 13 May 2014 09:13:31 +0200 Subject: [PATCH 21/66] Added russian to the list of available languages --- .../resources/com/sap/sailing/gwt/ui/AdminConsole.gwt.xml | 5 +++-- .../resources/com/sap/sailing/gwt/ui/Leaderboard.gwt.xml | 5 ++++- .../resources/com/sap/sailing/gwt/ui/RegattaOverview.gwt.xml | 2 ++ .../main/resources/com/sap/sailing/gwt/ui/Spectator.gwt.xml | 2 ++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/AdminConsole.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/AdminConsole.gwt.xml index 04e0c5ceeef..ba975c19318 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/AdminConsole.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/AdminConsole.gwt.xml @@ -55,8 +55,9 @@ - - + + + diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Leaderboard.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Leaderboard.gwt.xml index 687167bba31..8b6b3088382 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Leaderboard.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Leaderboard.gwt.xml @@ -29,8 +29,11 @@ + - + + + diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/RegattaOverview.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/RegattaOverview.gwt.xml index e67cdf024bb..0aab3682dce 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/RegattaOverview.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/RegattaOverview.gwt.xml @@ -42,6 +42,8 @@ + + diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Spectator.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Spectator.gwt.xml index a371794cfbc..7b83424fac7 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Spectator.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Spectator.gwt.xml @@ -29,6 +29,8 @@ + + From 780d1ec12c27c8b374915b9e6c1cddba7be411fa Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Tue, 13 May 2014 09:14:12 +0200 Subject: [PATCH 22/66] Somehow marineapi has been missing from the dependencies --- .../SailingServer (No Proxy, Jetty on 8889).launch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch index 52bb4de7034..6f0303bd02d 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch @@ -26,6 +26,6 @@ - + From 8f07d0f864f06a650610391973b59b021c68c30c Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Tue, 13 May 2014 09:52:13 +0200 Subject: [PATCH 23/66] Changed log level of some messages related to RaceLog add operation to fine --- .../com/sap/sailing/domain/racelog/impl/RaceLogImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/impl/RaceLogImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/impl/RaceLogImpl.java index 71e6db0ae76..4ebf7efd341 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/impl/RaceLogImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/impl/RaceLogImpl.java @@ -122,7 +122,7 @@ public class RaceLogImpl extends TrackImpl implements RaceLog { eventsById.put(event.getId(), event); notifyListenersAboutReceive(event); } else { - logger.warning(String.format("%s (%s) was not added to race log %s. Ignoring", event, event.getClass().getName(), getId())); + logger.fine(String.format("%s (%s) was not added to race log %s because it already existed there.", event, event.getClass().getName(), getId())); } return isAdded; } @@ -142,8 +142,8 @@ public class RaceLogImpl extends TrackImpl implements RaceLog { revokeIfNecessary(event); eventsById.put(event.getId(), event); } else { - logger.warning(String - .format("%s (%s) was not loaded into log. Ignoring", event, event.getClass().getName())); + logger.fine(String + .format("%s (%s) was not loaded into log because it already existed there.", event, event.getClass().getName())); } return isAdded; } @@ -202,7 +202,7 @@ public class RaceLogImpl extends TrackImpl implements RaceLog { revokeIfNecessary(event); notifyListenersAboutReceive(event); } else { - logger.warning(String.format("%s (%s) was not added to log. Ignoring", event, event.getClass().getName())); + logger.fine(String.format("%s (%s) was not added to log because it already eists there.", event, event.getClass().getName())); } return getEventsToDeliver(clientId, event); } From f8fe6fb326e54c881efdc3429f3fb51ecfeca0ef Mon Sep 17 00:00:00 2001 From: fmittag Date: Tue, 13 May 2014 11:16:13 +0200 Subject: [PATCH 24/66] added missing properties to russian properties file --- .../sailing/gwt/ui/client/StringMessages_ru.properties | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index 24f13cd0ac3..a86ef2e2029 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -956,3 +956,11 @@ importWind=Имопрт данных о ветре importWindTooltip=Снятие флажка с этой опции значительно снизит объем передаваемых данных. authorName=Имя автора authorPriority=Приоритет автора +competitorRegistrationsMissingProceed=todo +pleaseSelectAControlPoint=todo +twoMarkControlPoint=todo +registeredCompetitors=todo +competitorPool=todo +selectAtLeastOne=todo +selectRaces=todo +importFixes=todo From 818f5cade72bdb156f696110d9b9b3931300740b Mon Sep 17 00:00:00 2001 From: fmittag Date: Tue, 13 May 2014 11:23:17 +0200 Subject: [PATCH 25/66] missed a message in russian properties --- .../com/sap/sailing/gwt/ui/client/StringMessages_ru.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index a86ef2e2029..b87f4eed600 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -956,6 +956,7 @@ importWind=Имопрт данных о ветре importWindTooltip=Снятие флажка с этой опции значительно снизит объем передаваемых данных. authorName=Имя автора authorPriority=Приоритет автора +removeDenotation=todo competitorRegistrationsMissingProceed=todo pleaseSelectAControlPoint=todo twoMarkControlPoint=todo From b845fab6cc48207c578e2f432caa6bd0ec70309c Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Tue, 13 May 2014 11:44:02 +0200 Subject: [PATCH 26/66] Added simple launch template for amazon servers --- configuration/buildAndUpdateProduct.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/configuration/buildAndUpdateProduct.sh b/configuration/buildAndUpdateProduct.sh index 3c3b628b7e2..528d6230e88 100755 --- a/configuration/buildAndUpdateProduct.sh +++ b/configuration/buildAndUpdateProduct.sh @@ -241,6 +241,15 @@ if [[ "$@" == "release" ]]; then if [[ $OSGI_BUNDLE_NAME != "" ]]; then SIMPLE_VERSION_INFO="$OSGI_BUNDLE_NAME-$HEAD_DATE" fi + + echo " + MONGODB_NAME=myspecificevent + REPLICATION_CHANNEL=myspecificevent + SERVER_STARTUP_NOTIFY=simon.marcel.pamies@sap.com + SERVER_NAME=LIVE + USE_ENVIRONMENT=live-server + INSTALL_FROM_RELEASE=$SIMPLE_VERSION_INFO + " >> $ACDIR/amazon-launch-config.txt mkdir $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO `which tar` cvzf $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO/$SIMPLE_VERSION_INFO.tar.gz * From d47d0178f459abc1b7fd816e44b12d9cc050bbef Mon Sep 17 00:00:00 2001 From: Alexander Ries Date: Tue, 13 May 2014 11:31:10 +0000 Subject: [PATCH 27/66] Updated onboarding (markdown) --- wiki/onboarding.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/wiki/onboarding.md b/wiki/onboarding.md index 0c12fda37b7..00a55098e6d 100644 --- a/wiki/onboarding.md +++ b/wiki/onboarding.md @@ -104,4 +104,19 @@ To deploy an Android project (for example com.sap.sailing.racecommittee.app) to 3. Select your attached device in the device selection screen 4. The app should be started after deployment +####Stepps to consider for using other modules +=> For Eclipse Build + - MANIFEST.MF , add module names unter dependencies + - *.gwt.xml , add "" + - In DebugConfigurations => Classpaths, Add Sourcefolder where classes are you want to user from the module +=> For Maven Build + - pom.xml , Add Dependency to used module ie. + + com.sap.sailing + com.sap.sailing.domain.common + 1.0.0-SNAPSHOT + sources + + + See [RaceCommittee App](racecommittee-app) for more information regarding the mobile app. \ No newline at end of file From 21cc0297e5ce039803f63dab3d548cf16e6f3552 Mon Sep 17 00:00:00 2001 From: Alexander Ries Date: Tue, 13 May 2014 11:33:35 +0000 Subject: [PATCH 28/66] Updated onboarding (markdown) --- wiki/onboarding.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/wiki/onboarding.md b/wiki/onboarding.md index 00a55098e6d..0cffe16f26c 100644 --- a/wiki/onboarding.md +++ b/wiki/onboarding.md @@ -105,18 +105,18 @@ To deploy an Android project (for example com.sap.sailing.racecommittee.app) to 4. The app should be started after deployment ####Stepps to consider for using other modules -=> For Eclipse Build - - MANIFEST.MF , add module names unter dependencies - - *.gwt.xml , add "" - - In DebugConfigurations => Classpaths, Add Sourcefolder where classes are you want to user from the module -=> For Maven Build - - pom.xml , Add Dependency to used module ie. - - com.sap.sailing - com.sap.sailing.domain.common - 1.0.0-SNAPSHOT - sources - +1. For Eclipse Build + * MANIFEST.MF , add module names unter dependencies + * *.gwt.xml , add "" + * In DebugConfigurations => Classpaths, Add Sourcefolder where classes are you want to user from the module +2. For Maven Build + * pom.xml , Add Dependency to used module ie. + +com.sap.sailing +com.sap.sailing.domain.common +1.0.0-SNAPSHOT +sources + See [RaceCommittee App](racecommittee-app) for more information regarding the mobile app. \ No newline at end of file From f4addb78479421bf8d175176b9e097c0bd124236 Mon Sep 17 00:00:00 2001 From: Alexander Ries Date: Tue, 13 May 2014 11:34:12 +0000 Subject: [PATCH 29/66] Updated onboarding (markdown) From 5e20291de9d37b82eed8ad4ac9f6cfcb3f6ca522 Mon Sep 17 00:00:00 2001 From: Alexander Ries Date: Tue, 13 May 2014 11:35:39 +0000 Subject: [PATCH 30/66] Updated onboarding (markdown) --- wiki/onboarding.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wiki/onboarding.md b/wiki/onboarding.md index 0cffe16f26c..77934797ff5 100644 --- a/wiki/onboarding.md +++ b/wiki/onboarding.md @@ -107,16 +107,16 @@ To deploy an Android project (for example com.sap.sailing.racecommittee.app) to ####Stepps to consider for using other modules 1. For Eclipse Build * MANIFEST.MF , add module names unter dependencies - * *.gwt.xml , add "" + * *.gwt.xml , add `` * In DebugConfigurations => Classpaths, Add Sourcefolder where classes are you want to user from the module 2. For Maven Build * pom.xml , Add Dependency to used module ie. - +` com.sap.sailing com.sap.sailing.domain.common 1.0.0-SNAPSHOT sources - +` See [RaceCommittee App](racecommittee-app) for more information regarding the mobile app. \ No newline at end of file From 78b65ae11446f1c36e2c41f93652e33cd4d67e76 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 13 May 2014 13:46:59 +0200 Subject: [PATCH 31/66] the randomized wait interval in the SwissTiming connector killed a bunch of tests assuming faster response; withdrew the random wait; This was a workaround for bug 171 (see comment #2). We need to wait for what I expect to be a fix coming from SwissTiming. --- .../domain/swisstimingadapter/impl/SailMasterConnectorImpl.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterConnectorImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterConnectorImpl.java index 0b55670faf6..7c356bc833f 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterConnectorImpl.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterConnectorImpl.java @@ -508,8 +508,6 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement synchronized (ensureSocketIsOpenSemaphor) { while (!stopped && socket == null) { try { - // TODO see bug 171, comment #2; randomized delay to make it unlikely that two requests collide at the M2S server - Thread.sleep((long) (10000 /*ms*/ * Math.random())); // wait somewhere between 0s and 10s. logger.info("Opening socket to " + host + ":" + port + " and sending " + MessageType.OPN.name() + " message..."); socket = new Socket(host, port); From aab7cba02381477d57e2fb57bed5d5028c2c80cc Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 13 May 2014 15:02:32 +0200 Subject: [PATCH 32/66] more cleanup; use PositionDTO for LatLngs; --- .../gwt/ui/simulator/SimulatorEntryPoint.java | 1 + .../gwt/ui/simulator/SimulatorMap.java | 6 +- .../WindStreamletsCanvasOverlay.java | 6 +- .../gwt/ui/simulator/streamlets/GeoPos.java | 24 ---- .../gwt/ui/simulator/streamlets/Mercator.java | 17 ++- .../gwt/ui/simulator/streamlets/Particle.java | 4 +- .../ui/simulator/streamlets/RectField.java | 59 +++++----- .../{ => streamlets}/SimulatorField.java | 103 ++++++++---------- .../{ => streamlets}/SimulatorJSBundle.java | 4 +- .../gwt/ui/simulator/streamlets/Swarm.java | 31 +++--- .../ui/simulator/streamlets/VectorField.java | 20 ++-- .../simulator/{ => streamlets}/wind-data.js | 0 12 files changed, 126 insertions(+), 149 deletions(-) delete mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/GeoPos.java rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/{ => streamlets}/SimulatorField.java (65%) rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/{ => streamlets}/SimulatorJSBundle.java (69%) rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/{ => streamlets}/wind-data.js (100%) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java index 042381ef862..3a16138c492 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java @@ -15,6 +15,7 @@ import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel; import com.sap.sailing.gwt.ui.client.RemoteServiceMappingConstants; import com.sap.sailing.gwt.ui.client.SimulatorService; import com.sap.sailing.gwt.ui.client.SimulatorServiceAsync; +import com.sap.sailing.gwt.ui.simulator.streamlets.SimulatorJSBundle; import com.sap.sailing.simulator.util.SailingSimulatorConstants; public class SimulatorEntryPoint extends AbstractEntryPoint { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java index 980d555e9fb..cfa6afec00e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java @@ -211,9 +211,13 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ legendCanvasOverlay.setCurrent(result.getWindField().curSpeed,result.getWindField().curBearing); if (timePanel != null) { - timePanel.setMinMax(new Date(startTime), new Date(startTime + maxDurationTime), true); + Date endDate = new Date(startTime + maxDurationTime); + timePanel.setMinMax(new Date(startTime), endDate, true); timePanel.resetTimeSlider(); timePanel.timeChanged(windParams.getStartTime(), null); + if (windParams.isShowStreamlets2()) { + windStreamletsCanvasOverlay.setEndDate(endDate); + } } /** diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java index 9e1cb128c2d..0eb9261d174 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindStreamletsCanvasOverlay.java @@ -33,6 +33,7 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti private boolean visible = false; private Timer timer; + private Date endDate; private int nParticles; private Swarm swarm; @@ -206,6 +207,9 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti } + public void setEndDate(Date endDate) { + this.endDate = endDate; + } public void setWindField(final WindFieldDTO windFieldDTO) { this.windFieldDTO = windFieldDTO; @@ -281,7 +285,7 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti @Override public boolean shallStop() { - if (timer.getTime().getTime() >= this.windParams.getEndTime().getTime()) { + if (timer.getTime().getTime() >= this.endDate.getTime()) { return true; } else { return false; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/GeoPos.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/GeoPos.java deleted file mode 100644 index 44c6762d590..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/GeoPos.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.sap.sailing.gwt.ui.simulator.streamlets; - -import com.google.gwt.maps.client.base.LatLng; - -public class GeoPos { - - public double lat; - public double lng; - - public GeoPos() { - - } - - public GeoPos(double lat, double lng) { - this.lat = lat; - this.lng = lng; - } - - public GeoPos(LatLng pos) { - this.lat = pos.getLatitude(); - this.lng = pos.getLongitude(); - } - -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Mercator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Mercator.java index 2a97e4e6f8d..ed9eded619d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Mercator.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Mercator.java @@ -4,6 +4,7 @@ import com.google.gwt.canvas.dom.client.Context2d; import com.google.gwt.maps.client.MapWidget; import com.google.gwt.maps.client.base.LatLng; import com.google.gwt.maps.client.base.Point; +import com.sap.sailing.domain.common.dto.PositionDTO; import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; public class Mercator { @@ -33,12 +34,8 @@ public class Mercator { int canvasHeight = canvas.getCanvas().getOffsetHeight(); - GeoPos mapSW = new GeoPos(); - mapSW.lat = this.map.getBounds().getSouthWest().getLatitude(); - mapSW.lng = this.map.getBounds().getSouthWest().getLongitude(); - GeoPos mapNE = new GeoPos(); - mapNE.lat = this.map.getBounds().getNorthEast().getLatitude(); - mapNE.lng = this.map.getBounds().getNorthEast().getLongitude(); + PositionDTO mapSW = new PositionDTO(this.map.getBounds().getSouthWest().getLatitude(), this.map.getBounds().getSouthWest().getLongitude()); + PositionDTO mapNE = new PositionDTO(this.map.getBounds().getNorthEast().getLatitude(), this.map.getBounds().getNorthEast().getLongitude()); pointSW = this.sphere2plane(mapSW); pointNE = this.sphere2plane(mapNE); @@ -54,10 +51,10 @@ public class Mercator { this.delta = pointNE.y; } - public Vector sphere2plane(GeoPos p) { + public Vector sphere2plane(PositionDTO p) { Vector result = new Vector(); - result.x = p.lng*Math.PI/180.0; - double latsin = Math.sin(p.lat*Math.PI/180.0); + result.x = p.lngDeg*Math.PI/180.0; + double latsin = Math.sin(p.latDeg*Math.PI/180.0); result.y = 0.5*Math.log((1.0+latsin)/(1.0-latsin)); return result; } @@ -68,7 +65,7 @@ public class Mercator { return LatLng.newInstance(lat, lng); } - public Vector latlng2pixel(GeoPos p) { + public Vector latlng2pixel(PositionDTO p) { Vector proj = this.sphere2plane(p); proj.x = this.alpha * (proj.x - this.beta); proj.y = this.gamma * (proj.y - this.delta); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java index df819832b8d..cc8ede283e5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java @@ -1,9 +1,11 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; +import com.sap.sailing.domain.common.dto.PositionDTO; + public class Particle { - public GeoPos pos; + public PositionDTO pos; public Vector pxOld; public int alpha; public int age; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java index 4b6569ade5c..8486140b592 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java @@ -3,6 +3,7 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONParser; import com.google.gwt.maps.client.base.LatLng; +import com.sap.sailing.domain.common.dto.PositionDTO; public class RectField implements VectorField { @@ -18,8 +19,8 @@ public class RectField implements VectorField { public double visY0; public double visY1; - public GeoPos visSW; - public GeoPos visNE; + public PositionDTO visSW; + public PositionDTO visNE; private int w; private int h; @@ -33,8 +34,8 @@ public class RectField implements VectorField { this.y0 = y0; this.y1 = y1; - this.visSW = new GeoPos(0.0, 0.0); - this.visNE = new GeoPos(0.0, 0.0); + this.visSW = new PositionDTO(0.0, 0.0); + this.visNE = new PositionDTO(0.0, 0.0); this.field = field; this.w = field.length; @@ -93,26 +94,26 @@ public class RectField implements VectorField { public void prevStep() { } - public GeoPos getRandomPosition() { + public PositionDTO getRandomPosition() { double rndY = Math.random(); double rndX = Math.random(); - GeoPos result = new GeoPos(); - result.lat = rndY * this.visSW.lat + (1 - rndY) * this.visNE.lat; - result.lng = rndX * this.visSW.lng + (1 - rndX) * this.visNE.lng; + PositionDTO result = new PositionDTO(); + result.latDeg = rndY * this.visSW.latDeg + (1 - rndY) * this.visNE.latDeg; + result.lngDeg = rndX * this.visSW.lngDeg + (1 - rndX) * this.visNE.lngDeg; return result; } - public boolean inBounds(GeoPos p) { - return p.lng >= this.x0 && p.lng < this.x1 && p.lat >= this.y0 && p.lat < this.y1; + public boolean inBounds(PositionDTO p) { + return p.lngDeg >= this.x0 && p.lngDeg < this.x1 && p.latDeg >= this.y0 && p.latDeg < this.y1; } - public Vector interpolate(GeoPos p) { - int na = (int)Math.floor(p.lng); - int nb = (int)Math.floor(p.lat); - int ma = (int)Math.ceil(p.lng); - int mb = (int)Math.ceil(p.lat); - double fa = p.lng - na; - double fb = p.lat - nb; + public Vector interpolate(PositionDTO p) { + int na = (int)Math.floor(p.lngDeg); + int nb = (int)Math.floor(p.latDeg); + int ma = (int)Math.ceil(p.lngDeg); + int mb = (int)Math.ceil(p.latDeg); + double fa = p.lngDeg - na; + double fb = p.latDeg - nb; Vector result = new Vector(); result.x = this.field[na][nb].x * (1 - fa) * (1 - fb) + this.field[ma][nb].x * fa * (1 - fb) + this.field[na][mb].x * (1 - fa) * fb + this.field[ma][mb].x * fa * fb; @@ -122,11 +123,11 @@ public class RectField implements VectorField { } - public Vector getVector(GeoPos p) { - GeoPos q = new GeoPos(); - q.lng = (this.w - 1 - 1e-6) * (p.lng - this.x0) / (this.x1 - this.x0); - q.lat = (this.h - 1 - 1e-6) * (p.lat - this.y0) / (this.y1 - this.y0); - if ((q.lng < 0)||(q.lng > (this.w-1))||(q.lat < 0)||(q.lat > (this.h-1))) { + public Vector getVector(PositionDTO p) { + PositionDTO q = new PositionDTO(); + q.lngDeg = (this.w - 1 - 1e-6) * (p.lngDeg - this.x0) / (this.x1 - this.x0); + q.latDeg = (this.h - 1 - 1e-6) * (p.latDeg - this.y0) / (this.y1 - this.y0); + if ((q.lngDeg < 0)||(q.lngDeg > (this.w-1))||(q.latDeg < 0)||(q.latDeg > (this.h-1))) { return null; } else { return this.interpolate(q); @@ -145,7 +146,7 @@ public class RectField implements VectorField { return 0.9 * Math.pow(1.7, Math.min(1.0, 6.0 - zoomLevel)); } - public double particleWeight(GeoPos p, Vector v) { + public double particleWeight(PositionDTO p, Vector v) { return 1.0 - v.length() / this.maxLength; } @@ -164,19 +165,19 @@ public class RectField implements VectorField { return 1.0; } - public GeoPos getFieldNE() { - return new GeoPos(Math.max(this.y0, this.y1), Math.max(this.x0, this.x1)); + public PositionDTO getFieldNE() { + return new PositionDTO(Math.max(this.y0, this.y1), Math.max(this.x0, this.x1)); } - public GeoPos getFieldSW() { - return new GeoPos(Math.min(this.y0, this.y1), Math.min(this.x0, this.x1)); + public PositionDTO getFieldSW() { + return new PositionDTO(Math.min(this.y0, this.y1), Math.min(this.x0, this.x1)); } - public void setVisNE(GeoPos visNE) { + public void setVisNE(PositionDTO visNE) { this.visNE = visNE; } - public void setVisSW(GeoPos visSW) { + public void setVisSW(PositionDTO visSW) { this.visSW = visSW; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java similarity index 65% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorField.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java index 6a7277d0707..9d6d9d5f25f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java @@ -1,24 +1,20 @@ -package com.sap.sailing.gwt.ui.simulator; +package com.sap.sailing.gwt.ui.simulator.streamlets; import java.util.List; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONParser; +import com.sap.sailing.domain.common.dto.PositionDTO; import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO; -import com.sap.sailing.gwt.ui.simulator.streamlets.GeoPos; -import com.sap.sailing.gwt.ui.simulator.streamlets.Index; -import com.sap.sailing.gwt.ui.simulator.streamlets.Neighbors; -import com.sap.sailing.gwt.ui.simulator.streamlets.Vector; -import com.sap.sailing.gwt.ui.simulator.streamlets.VectorField; public class SimulatorField implements VectorField { private boolean swarmDebug = false; - private GeoPos rcStart; - private GeoPos rcEnd; + private PositionDTO rcStart; + private PositionDTO rcEnd; private int resY; private int resX; @@ -29,9 +25,9 @@ public class SimulatorField implements VectorField { private double bdXi; private double bdPhi; - private GeoPos bdA; - private GeoPos bdB; - private GeoPos bdC; + private PositionDTO bdA; + private PositionDTO bdB; + private PositionDTO bdC; private double xScale; @@ -40,17 +36,17 @@ public class SimulatorField implements VectorField { private double y0; private double y1; - public GeoPos visSW; - public GeoPos visNE; + public PositionDTO visSW; + public PositionDTO visNE; private double maxLength; private double particleFactor; private double lngScale; - private GeoPos nvY; - private GeoPos nvX; - private GeoPos gvX; + private PositionDTO nvY; + private PositionDTO nvX; + private PositionDTO gvX; private double[][][] data; private int step; @@ -62,13 +58,8 @@ public class SimulatorField implements VectorField { String parseString = windData.windDataJSON.substring(18, windData.windDataJSON.length()-1) + "}"; JSONObject baseData = JSONParser.parseLenient(parseString).isObject(); - this.rcStart = new GeoPos(); - this.rcStart.lat = baseData.get("rcStart").isObject().get("lat").isNumber().doubleValue(); - this.rcStart.lng = baseData.get("rcStart").isObject().get("lng").isNumber().doubleValue(); - - this.rcEnd = new GeoPos(); - this.rcEnd.lat = baseData.get("rcEnd").isObject().get("lat").isNumber().doubleValue(); - this.rcEnd.lng = baseData.get("rcEnd").isObject().get("lng").isNumber().doubleValue(); + this.rcStart = new PositionDTO(baseData.get("rcStart").isObject().get("lat").isNumber().doubleValue(), baseData.get("rcStart").isObject().get("lng").isNumber().doubleValue()); + this.rcEnd = new PositionDTO(baseData.get("rcEnd").isObject().get("lat").isNumber().doubleValue(), baseData.get("rcEnd").isObject().get("lng").isNumber().doubleValue()); this.resY = (int)baseData.get("resY").isNumber().doubleValue(); this.resX = (int)baseData.get("resX").isNumber().doubleValue(); @@ -78,8 +69,8 @@ public class SimulatorField implements VectorField { this.bdXi = (this.borderY + 0.5) / (this.resY - 1); this.bdPhi = 1.0 + 2*this.bdXi; - this.bdA = new GeoPos(this.rcEnd.lat+(this.rcEnd.lat-this.rcStart.lat)*this.bdXi, this.rcEnd.lng+(this.rcEnd.lng-this.rcStart.lng)*this.bdXi); - this.bdB = new GeoPos((this.rcStart.lat-this.rcEnd.lat)*this.bdPhi, (this.rcStart.lng-this.rcEnd.lng)*this.bdPhi); + this.bdA = new PositionDTO(this.rcEnd.latDeg+(this.rcEnd.latDeg-this.rcStart.latDeg)*this.bdXi, this.rcEnd.lngDeg+(this.rcEnd.lngDeg-this.rcStart.lngDeg)*this.bdXi); + this.bdB = new PositionDTO((this.rcStart.latDeg-this.rcEnd.latDeg)*this.bdPhi, (this.rcStart.lngDeg-this.rcEnd.lngDeg)*this.bdPhi); this.xScale = baseData.get("xScale").isNumber().doubleValue(); @@ -88,8 +79,8 @@ public class SimulatorField implements VectorField { this.y0 = baseData.get("boundsSW").isObject().get("lat").isNumber().doubleValue(); this.y1 = baseData.get("boundsNE").isObject().get("lat").isNumber().doubleValue(); - this.visSW = new GeoPos(0.0, 0.0); - this.visNE = new GeoPos(0.0, 0.0); + this.visSW = new PositionDTO(0.0, 0.0); + this.visNE = new PositionDTO(0.0, 0.0); List gridData = windData.getMatrix(); @@ -120,31 +111,31 @@ public class SimulatorField implements VectorField { this.particleFactor = 2.0; - double latAvg = (this.rcEnd.lat + this.rcStart.lat) / 2.; + double latAvg = (this.rcEnd.latDeg + this.rcStart.latDeg) / 2.; this.lngScale = Math.cos(latAvg * Math.PI / 180.0); - double difLat = this.rcEnd.lat - this.rcStart.lat; - double difLng = (this.rcEnd.lng - this.rcStart.lng) * this.lngScale; + double difLat = this.rcEnd.latDeg - this.rcStart.latDeg; + double difLng = (this.rcEnd.lngDeg - this.rcStart.lngDeg) * this.lngScale; double difLen = Math.sqrt(difLat*difLat + difLng*difLng); - this.nvY = new GeoPos(difLat/difLen/difLen*(this.resY-1), difLng/difLen/difLen*(this.resY-1)); + this.nvY = new PositionDTO(difLat/difLen/difLen*(this.resY-1), difLng/difLen/difLen*(this.resY-1)); double nrmLat = -difLng/difLen; double nrmLng = difLat/difLen; - this.nvX = new GeoPos( nrmLat/this.xScale/difLen*(this.resX-1), nrmLng/this.xScale/difLen*(this.resX-1) ); - this.gvX = new GeoPos( nrmLat*this.xScale*difLen, nrmLng/this.lngScale*this.xScale*difLen ); - this.bdC = new GeoPos( this.gvX.lat*(this.resX+2*this.borderX-1)/(this.resX-1), this.gvX.lng*(this.resX+2*this.borderX-1)/(this.resX-1) ); + this.nvX = new PositionDTO( nrmLat/this.xScale/difLen*(this.resX-1), nrmLng/this.xScale/difLen*(this.resX-1) ); + this.gvX = new PositionDTO( nrmLat*this.xScale*difLen, nrmLng/this.lngScale*this.xScale*difLen ); + this.bdC = new PositionDTO( this.gvX.latDeg*(this.resX+2*this.borderX-1)/(this.resX-1), this.gvX.lngDeg*(this.resX+2*this.borderX-1)/(this.resX-1) ); } public static native void console(String msg) /*-{ console.log(msg); }-*/; - public GeoPos getRandomPosition() { + public PositionDTO getRandomPosition() { double rndY = Math.random(); double rndX = Math.random() - 0.5; - GeoPos result = new GeoPos(); - result.lat = this.bdA.lat + rndY * this.bdB.lat + rndX * this.bdC.lat; - result.lng = this.bdA.lng + rndY * this.bdB.lng + rndX * this.bdC.lng; + PositionDTO result = new PositionDTO(); + result.latDeg = this.bdA.latDeg + rndY * this.bdB.latDeg + rndX * this.bdC.latDeg; + result.lngDeg = this.bdA.lngDeg + rndY * this.bdB.lngDeg + rndX * this.bdC.lngDeg; if (swarmDebug&&(!this.inBounds(result))) { console("random-position: out of bounds"); @@ -154,14 +145,14 @@ public class SimulatorField implements VectorField { } - public boolean inBounds(GeoPos p) { + public boolean inBounds(PositionDTO p) { Index idx = this.getIndex(p); boolean inBool = (idx.x >= 0) && (idx.x < (this.resX+2*this.borderX)) && (idx.y >= 0) && (idx.y < (this.resY+2*this.borderY)); return inBool; } - public Vector interpolate(GeoPos p) { + public Vector interpolate(PositionDTO p) { Neighbors idx = this.getNeighbors(p); @@ -201,30 +192,30 @@ public class SimulatorField implements VectorField { } } - public Vector getVector(GeoPos p) { + public Vector getVector(PositionDTO p) { return this.interpolate(p); } - public Index getIndex(GeoPos p) { + public Index getIndex(PositionDTO p) { // calculate grid indexes - GeoPos posR = new GeoPos( p.lat - this.rcStart.lat, (p.lng - this.rcStart.lng) * this.lngScale ); + PositionDTO posR = new PositionDTO( p.latDeg - this.rcStart.latDeg, (p.lngDeg - this.rcStart.lngDeg) * this.lngScale ); // closest grid point - long yIdx = Math.round( posR.lat * this.nvY.lat + posR.lng * this.nvY.lng ) + this.borderY; - long xIdx = Math.round( posR.lat * this.nvX.lat + posR.lng * this.nvX.lng + (this.resX - 1) / 2. ) + this.borderX; + long yIdx = Math.round( posR.latDeg * this.nvY.latDeg + posR.lngDeg * this.nvY.lngDeg ) + this.borderY; + long xIdx = Math.round( posR.latDeg * this.nvX.latDeg + posR.lngDeg * this.nvX.lngDeg + (this.resX - 1) / 2. ) + this.borderX; return new Index( xIdx, yIdx ); } - public Neighbors getNeighbors(GeoPos p) { + public Neighbors getNeighbors(PositionDTO p) { // calculate grid indexes - GeoPos posR = new GeoPos( p.lat - this.rcStart.lat, (p.lng - this.rcStart.lng) * this.lngScale ); + PositionDTO posR = new PositionDTO( p.latDeg - this.rcStart.latDeg, (p.lngDeg - this.rcStart.lngDeg) * this.lngScale ); // surrounding grid points - double yFlt = posR.lat * this.nvY.lat + posR.lng * this.nvY.lng + this.borderY; - double xFlt = posR.lat * this.nvX.lat + posR.lng * this.nvX.lng + (this.resX - 1) / 2. + this.borderX; + double yFlt = posR.latDeg * this.nvY.latDeg + posR.lngDeg * this.nvY.lngDeg + this.borderY; + double xFlt = posR.latDeg * this.nvX.latDeg + posR.lngDeg * this.nvX.lngDeg + (this.resX - 1) / 2. + this.borderX; double yBot = Math.floor( yFlt ); double xBot = Math.floor( xFlt ); double yTop = Math.ceil( yFlt ); @@ -261,7 +252,7 @@ public class SimulatorField implements VectorField { return 0.08 * Math.pow(1.6, Math.min(1.0, 6.0 - zoomLevel)); } - public double particleWeight(GeoPos p, Vector v) { + public double particleWeight(PositionDTO p, Vector v) { return v.length() / this.maxLength + 0.1; } @@ -280,19 +271,19 @@ public class SimulatorField implements VectorField { return 1.0; } - public GeoPos getFieldNE() { - return new GeoPos(Math.max(this.y0, this.y1), Math.max(this.x0, this.x1)); + public PositionDTO getFieldNE() { + return new PositionDTO(Math.max(this.y0, this.y1), Math.max(this.x0, this.x1)); } - public GeoPos getFieldSW() { - return new GeoPos(Math.min(this.y0, this.y1), Math.min(this.x0, this.x1)); + public PositionDTO getFieldSW() { + return new PositionDTO(Math.min(this.y0, this.y1), Math.min(this.x0, this.x1)); } - public void setVisNE(GeoPos visNE) { + public void setVisNE(PositionDTO visNE) { this.visNE = visNE; } - public void setVisSW(GeoPos visSW) { + public void setVisSW(PositionDTO visSW) { this.visSW = visSW; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorJSBundle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorJSBundle.java similarity index 69% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorJSBundle.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorJSBundle.java index f59f76ef938..ba6a52caa6f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorJSBundle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorJSBundle.java @@ -1,4 +1,4 @@ -package com.sap.sailing.gwt.ui.simulator; +package com.sap.sailing.gwt.ui.simulator.streamlets; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.TextResource; @@ -8,7 +8,7 @@ public interface SimulatorJSBundle extends ClientBundle { //@Source("wind-streamlets.js") //TextResource windStreamletsJS(); - @Source("com/sap/sailing/gwt/ui/simulator/wind-data.js") + @Source("com/sap/sailing/gwt/ui/simulator/streamlets/wind-data.js") TextResource windStreamletsDataJS(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java index 344907a9363..3c0fd653943 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java @@ -7,9 +7,8 @@ import com.google.gwt.canvas.dom.client.Context2d; import com.google.gwt.core.client.GWT; import com.google.gwt.maps.client.MapWidget; import com.google.gwt.user.client.Timer; +import com.sap.sailing.domain.common.dto.PositionDTO; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; -import com.sap.sailing.gwt.ui.simulator.SimulatorField; -import com.sap.sailing.gwt.ui.simulator.SimulatorJSBundle; import com.sap.sailing.gwt.ui.simulator.WindStreamletsCanvasOverlay; import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; @@ -28,8 +27,8 @@ public class Swarm { private boolean swarmOffScreen = false; private int swarmPause = 0; private boolean swarmContinue = true; - private GeoPos boundsNE; - private GeoPos boundsSW; + private PositionDTO boundsNE; + private PositionDTO boundsSW; public Swarm(FullCanvasOverlay fullcanvas, MapWidget map) { this.fullcanvas = fullcanvas; @@ -68,7 +67,7 @@ public class Swarm { ctxt.setFillStyle("red"); /*Vector p = this.projection.latlng2pixel(this.field.getFieldNE()); - ctxt.fillRect(p.x,p.y,5.0,5.0); + ctxt.fillRect(p.x,p.y,5.0,5.0); p = this.projection.latlng2pixel(this.field.getFieldSW()); ctxt.fillRect(p.x,p.y,5.0,5.0);*/ @@ -130,11 +129,11 @@ public class Swarm { public void updateBounds() { - GeoPos mapNE = new GeoPos(map.getBounds().getNorthEast()); - GeoPos mapSW = new GeoPos(map.getBounds().getSouthWest()); + PositionDTO mapNE = new PositionDTO(map.getBounds().getNorthEast().getLatitude(), map.getBounds().getNorthEast().getLongitude()); + PositionDTO mapSW = new PositionDTO(map.getBounds().getSouthWest().getLatitude(), map.getBounds().getSouthWest().getLongitude()); - GeoPos fieldNE = this.field.getFieldNE(); - GeoPos fieldSW = this.field.getFieldSW(); + PositionDTO fieldNE = this.field.getFieldNE(); + PositionDTO fieldSW = this.field.getFieldSW(); Vector visibleNE = this.isVisible(fieldNE); Vector visibleSW = this.isVisible(fieldSW); @@ -156,9 +155,9 @@ public class Swarm { if ((!useBoundsNorth)&&(!useBoundsEast)) { this.boundsNE = mapNE; } else if (!useBoundsNorth) { - this.boundsNE = new GeoPos(mapNE.lat, fieldNE.lng); + this.boundsNE = new PositionDTO(mapNE.latDeg, fieldNE.lngDeg); } else if (!useBoundsEast) { - this.boundsNE = new GeoPos(fieldNE.lat, mapNE.lng); + this.boundsNE = new PositionDTO(fieldNE.latDeg, mapNE.lngDeg); } else { this.boundsNE = fieldNE; } @@ -166,9 +165,9 @@ public class Swarm { if ((!useBoundsSouth)&&(!useBoundsWest)) { this.boundsSW = mapSW; } else if (!useBoundsSouth) { - this.boundsSW = new GeoPos(mapSW.lat, fieldSW.lng); + this.boundsSW = new PositionDTO(mapSW.latDeg, fieldSW.lngDeg); } else if (!useBoundsWest) { - this.boundsSW = new GeoPos(fieldSW.lat, mapSW.lng); + this.boundsSW = new PositionDTO(fieldSW.latDeg, mapSW.lngDeg); } else { this.boundsSW = fieldSW; } @@ -188,7 +187,7 @@ public class Swarm { //console("#particles: "+this.nParticles + " at " + (boundsWidthpx) +"x" + (boundsHeightpx) + "px (" + (boundsWidthpx * boundsHeightpx) + " pixels)"); }; - public Vector isVisible(GeoPos pos) { + public Vector isVisible(PositionDTO pos) { // test for visibility of swarm Vector proj = this.projection.latlng2pixel(pos); @@ -264,8 +263,8 @@ public class Swarm { for(int idx=0; idx 0) && (particle.v != null)) { - particle.pos.lat = particle.pos.lat + speed*particle.v.y; - particle.pos.lng = particle.pos.lng + speed*particle.v.x; + particle.pos.latDeg = particle.pos.latDeg + speed*particle.v.y; + particle.pos.lngDeg = particle.pos.lngDeg + speed*particle.v.x; double s = particle.v.length() / field.getMaxLength(); particle.alpha = (int)Math.min(255, 90 + Math.round(350 * s)); particle.age--; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java index e1b92e95bfb..5e106988a99 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java @@ -1,33 +1,35 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; +import com.sap.sailing.domain.common.dto.PositionDTO; + public interface VectorField { - public GeoPos getRandomPosition(); + public PositionDTO getRandomPosition(); - public boolean inBounds(GeoPos p); + public boolean inBounds(PositionDTO p); - public Vector interpolate(GeoPos p); + public Vector interpolate(PositionDTO p); - public Vector getVector(GeoPos p); + public Vector getVector(PositionDTO p); public double getMaxLength(); public double motionScale(int zoomLevel); - public double particleWeight(GeoPos p, Vector v); + public double particleWeight(PositionDTO p, Vector v); public String[] getColors(); public double lineWidth(int alpha); - public GeoPos getFieldNE(); + public PositionDTO getFieldNE(); - public GeoPos getFieldSW(); + public PositionDTO getFieldSW(); - public void setVisNE(GeoPos visNE); + public void setVisNE(PositionDTO visNE); - public void setVisSW(GeoPos visSW); + public void setVisSW(PositionDTO visSW); public double getParticleFactor(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/wind-data.js b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/wind-data.js similarity index 100% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/wind-data.js rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/wind-data.js From c479814e57193a3cf7d10c66809bd1ed93568f7a Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Tue, 13 May 2014 15:18:45 +0200 Subject: [PATCH 33/66] Added russian language to additional resources --- .../main/resources/com/sap/sailing/gwt/ui/DataMining.gwt.xml | 1 + .../src/main/resources/com/sap/sailing/gwt/ui/RaceBoard.gwt.xml | 2 ++ .../src/main/resources/com/sap/sailing/gwt/ui/Simulator.gwt.xml | 2 ++ .../src/main/resources/com/sap/sailing/gwt/ui/TvView.gwt.xml | 2 ++ 4 files changed, 7 insertions(+) diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/DataMining.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/DataMining.gwt.xml index 6ed344d4767..c45a84e3e78 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/DataMining.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/DataMining.gwt.xml @@ -21,6 +21,7 @@ + diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/RaceBoard.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/RaceBoard.gwt.xml index 389070a02f2..6d443d297c8 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/RaceBoard.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/RaceBoard.gwt.xml @@ -33,6 +33,8 @@ + + diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Simulator.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Simulator.gwt.xml index 4e96732772d..fb84c52a10f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Simulator.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Simulator.gwt.xml @@ -31,6 +31,8 @@ + + diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/TvView.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/TvView.gwt.xml index 4d5834e9924..e05d56722df 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/TvView.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/TvView.gwt.xml @@ -31,6 +31,8 @@ + + From e8578a35238b911c4185c3722a3d88c1b075d5a4 Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Tue, 13 May 2014 15:44:14 +0200 Subject: [PATCH 34/66] Fixed translatable string for leaderboard --- .../sailing/gwt/ui/regattaoverview/RegattaOverviewPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaOverviewPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaOverviewPanel.java index 3372ab87d22..1e84a977abe 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaOverviewPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/RegattaOverviewPanel.java @@ -202,7 +202,7 @@ public class RegattaOverviewPanel extends SimplePanel { } private CheckBox addLeaderboardEnablerButton() { - final CheckBox checkBox = new CheckBox("Leaderboard"); + final CheckBox checkBox = new CheckBox(stringMessages.leaderboard()); checkBox.getElement().getStyle().setFloat(Style.Float.LEFT); From 45bf069f252fc5706fbe295a43880ca462f01c91 Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Tue, 13 May 2014 16:45:11 +0200 Subject: [PATCH 35/66] Make sure that we have at least the english translation --- .../gwt/ui/client/StringMessages_ru.properties | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index b87f4eed600..1119bdeaafd 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -956,12 +956,12 @@ importWind=Имопрт данных о ветре importWindTooltip=Снятие флажка с этой опции значительно снизит объем передаваемых данных. authorName=Имя автора authorPriority=Приоритет автора -removeDenotation=todo -competitorRegistrationsMissingProceed=todo -pleaseSelectAControlPoint=todo -twoMarkControlPoint=todo -registeredCompetitors=todo -competitorPool=todo -selectAtLeastOne=todo -selectRaces=todo -importFixes=todo +removeDenotation=Remove Denotation +competitorRegistrationsMissingProceed=At least one of the selected races ({0}) is missing competitor registrations. Proceed anyway? +pleaseSelectAControlPoint=Please select a control point +twoMarkControlPoint=2-mark-controlpoint +registeredCompetitors=Registered Competitors +competitorPool=Competitor Pool +selectAtLeastOne=Select at least one +selectRaces=Select the Races +importFixes=Import Fixes \ No newline at end of file From 2cd1a088dfeee4be50eec81759a9471c1b424419 Mon Sep 17 00:00:00 2001 From: fmittag Date: Tue, 13 May 2014 16:52:03 +0200 Subject: [PATCH 36/66] added highpoint scoring system where the winner gets 5 points --- .../sailing/domain/common/ScoringSchemeType.java | 2 +- .../domain/base/impl/DomainFactoryImpl.java | 3 +++ .../impl/HighPointWinnerGetsFive.java | 16 ++++++++++++++++ .../sailing/gwt/ui/client/StringMessages.java | 1 + .../gwt/ui/client/StringMessages.properties | 1 + .../gwt/ui/client/StringMessages_de.properties | 1 + .../gwt/ui/client/StringMessages_ru.properties | 2 ++ .../leaderboard/ScoringSchemeTypeFormatter.java | 2 ++ 8 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointWinnerGetsFive.java diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ScoringSchemeType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ScoringSchemeType.java index 687511c2430..0102c273dda 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ScoringSchemeType.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ScoringSchemeType.java @@ -2,5 +2,5 @@ package com.sap.sailing.domain.common; public enum ScoringSchemeType { LOW_POINT, HIGH_POINT, HIGH_POINT_ESS_OVERALL, HIGH_POINT_LAST_BREAKS_TIE, HIGH_POINT_FIRST_GETS_TEN, - HIGH_POINT_FIRST_GETS_ONE, LOW_POINT_WINNER_GETS_ZERO, HIGH_POINT_WINNER_GETS_SIX + HIGH_POINT_FIRST_GETS_ONE, LOW_POINT_WINNER_GETS_ZERO, HIGH_POINT_WINNER_GETS_SIX, HIGH_POINT_WINNER_GETS_FIVE } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java index e2fa9327610..6c2d58948d6 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java @@ -35,6 +35,7 @@ import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOver import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10LastBreaksTie; import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets1LastBreaksTie; import com.sap.sailing.domain.leaderboard.impl.HighPointLastBreaksTie; +import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsFive; import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsSix; import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPointWinnerGetsZero; @@ -87,6 +88,8 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl implements Domain return new HighPointFirstGets10LastBreaksTie(); case LOW_POINT_WINNER_GETS_ZERO: return new LowPointWinnerGetsZero(); + case HIGH_POINT_WINNER_GETS_FIVE: + return new HighPointWinnerGetsFive(); case HIGH_POINT_WINNER_GETS_SIX: return new HighPointWinnerGetsSix(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointWinnerGetsFive.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointWinnerGetsFive.java new file mode 100644 index 00000000000..a66be199a7d --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointWinnerGetsFive.java @@ -0,0 +1,16 @@ +package com.sap.sailing.domain.leaderboard.impl; + +import com.sap.sailing.domain.common.ScoringSchemeType; + +public class HighPointWinnerGetsFive extends HighPointFirstGetsFixedScore { + private static final long serialVersionUID = 1367473387431601943L; + + public HighPointWinnerGetsFive() { + super(/* scoreForRaceWinner */ 5); + } + + @Override + public ScoringSchemeType getType() { + return ScoringSchemeType.HIGH_POINT_WINNER_GETS_FIVE; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 41865209368..4ad35e221df 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -374,6 +374,7 @@ public interface StringMessages extends Messages { String scoringSchemeLowPointSystem(); String scoringSchemeHighPointSystem(); String scoringSchemeLowPointWinnerGetsZero(); + String scoringSchemeWinnerGetsFive(); String scoringSchemeWinnerGetsSix(); String scoringSystem(); String createFlexibleLeaderboard(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 68c37dd0ed6..04b86acaa98 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -403,6 +403,7 @@ scoringSchemeHighPointSystem=High Point System scoringSchemeHighPointEssOverall=High Point System ESS Overall scoringSchemeHighPointLastBreaksTie=High Point System, Last Race Breaks Tie scoringSchemeLowPointWinnerGetsZero=Low Point System, Winner Gets Zero +scoringSchemeWinnerGetsFive=High Point System, Winner Gets 5 Points scoringSchemeWinnerGetsSix=High Point System, Winner Gets 6 Points scoringSystem=Scoring system createFlexibleLeaderboard=Create flexible leaderboard diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 511eb97acf9..37ccbd945b1 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -404,6 +404,7 @@ scoringSchemeHighPointSystem=High Point System scoringSchemeHighPointEssOverall=High Point System ESS Gesamtwertung scoringSchemeHighPointLastBreaksTie=High Point System, letztes Rennen entscheidet bei Gleichstand scoringSchemeLowPointWinnerGetsZero=Low Point System, Gewinner erhält 0 Punkte +scoringSchemeWinnerGetsFive=High Point System, Gewinner erhält 5 Punkte scoringSchemeWinnerGetsSix=High Point System, Gewinner erhält 6 Punkte scoringSystem=Bepunktungssystem createFlexibleLeaderboard=Flexible Rangliste anlegen diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index b87f4eed600..755acee2126 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -397,6 +397,8 @@ scoringSchemeHighPointSystem=Высокобальная система подс scoringSchemeHighPointEssOverall=Высокобальная система подсчета, итоговые баллы экстремальной серии ESS scoringSchemeHighPointLastBreaksTie=Высокобальная система подсчета, тай-брейк на последней гонке scoringSchemeLowPointWinnerGetsZero=Низкобальная система подсчета очков, победитель получает ноль очков +scoringSchemeWinnerGetsFive=Высокобальная система подсчета, победитель получает 5 очков +scoringSchemeWinnerGetsSix=Высокобальная система подсчета, победитель получает 6 очков scoringSystem=Система подсчета очков createFlexibleLeaderboard=Создание адаптивной таблицы лидеров createRegattaLeaderboard=Создание таблицы лидеров регаты diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ScoringSchemeTypeFormatter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ScoringSchemeTypeFormatter.java index 9c9f225bd67..113e14d586e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ScoringSchemeTypeFormatter.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ScoringSchemeTypeFormatter.java @@ -20,6 +20,8 @@ public class ScoringSchemeTypeFormatter { return stringMessages.scoringSchemeHighPointFirstGetsTen(); case LOW_POINT_WINNER_GETS_ZERO: return stringMessages.scoringSchemeLowPointWinnerGetsZero(); + case HIGH_POINT_WINNER_GETS_FIVE: + return stringMessages.scoringSchemeWinnerGetsFive(); case HIGH_POINT_WINNER_GETS_SIX: return stringMessages.scoringSchemeWinnerGetsSix(); } From ea7d8ff0d5740baf33a3f486bc079bccf2d0f351 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 13 May 2014 17:07:53 +0200 Subject: [PATCH 37/66] replace PositionDTO with Position; --- .../gwt/ui/simulator/streamlets/Mercator.java | 15 +-- .../gwt/ui/simulator/streamlets/Particle.java | 4 +- .../ui/simulator/streamlets/RectField.java | 62 ++++++------ .../simulator/streamlets/SimulatorField.java | 94 ++++++++++--------- .../gwt/ui/simulator/streamlets/Swarm.java | 30 +++--- .../ui/simulator/streamlets/VectorField.java | 20 ++-- 6 files changed, 115 insertions(+), 110 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Mercator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Mercator.java index ed9eded619d..4269ec0663b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Mercator.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Mercator.java @@ -4,7 +4,8 @@ import com.google.gwt.canvas.dom.client.Context2d; import com.google.gwt.maps.client.MapWidget; import com.google.gwt.maps.client.base.LatLng; import com.google.gwt.maps.client.base.Point; -import com.sap.sailing.domain.common.dto.PositionDTO; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; public class Mercator { @@ -34,8 +35,8 @@ public class Mercator { int canvasHeight = canvas.getCanvas().getOffsetHeight(); - PositionDTO mapSW = new PositionDTO(this.map.getBounds().getSouthWest().getLatitude(), this.map.getBounds().getSouthWest().getLongitude()); - PositionDTO mapNE = new PositionDTO(this.map.getBounds().getNorthEast().getLatitude(), this.map.getBounds().getNorthEast().getLongitude()); + Position mapSW = new DegreePosition(this.map.getBounds().getSouthWest().getLatitude(), this.map.getBounds().getSouthWest().getLongitude()); + Position mapNE = new DegreePosition(this.map.getBounds().getNorthEast().getLatitude(), this.map.getBounds().getNorthEast().getLongitude()); pointSW = this.sphere2plane(mapSW); pointNE = this.sphere2plane(mapNE); @@ -51,10 +52,10 @@ public class Mercator { this.delta = pointNE.y; } - public Vector sphere2plane(PositionDTO p) { + public Vector sphere2plane(Position p) { Vector result = new Vector(); - result.x = p.lngDeg*Math.PI/180.0; - double latsin = Math.sin(p.latDeg*Math.PI/180.0); + result.x = p.getLngDeg()*Math.PI/180.0; + double latsin = Math.sin(p.getLatDeg()*Math.PI/180.0); result.y = 0.5*Math.log((1.0+latsin)/(1.0-latsin)); return result; } @@ -65,7 +66,7 @@ public class Mercator { return LatLng.newInstance(lat, lng); } - public Vector latlng2pixel(PositionDTO p) { + public Vector latlng2pixel(Position p) { Vector proj = this.sphere2plane(p); proj.x = this.alpha * (proj.x - this.beta); proj.y = this.gamma * (proj.y - this.delta); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java index cc8ede283e5..59d0c9cb183 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Particle.java @@ -1,11 +1,11 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; -import com.sap.sailing.domain.common.dto.PositionDTO; +import com.sap.sailing.domain.common.Position; public class Particle { - public PositionDTO pos; + public Position pos; public Vector pxOld; public int alpha; public int age; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java index 8486140b592..8c21e3980b6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/RectField.java @@ -3,7 +3,8 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONParser; import com.google.gwt.maps.client.base.LatLng; -import com.sap.sailing.domain.common.dto.PositionDTO; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.impl.DegreePosition; public class RectField implements VectorField { @@ -19,8 +20,8 @@ public class RectField implements VectorField { public double visY0; public double visY1; - public PositionDTO visSW; - public PositionDTO visNE; + public Position visSW; + public Position visNE; private int w; private int h; @@ -34,8 +35,8 @@ public class RectField implements VectorField { this.y0 = y0; this.y1 = y1; - this.visSW = new PositionDTO(0.0, 0.0); - this.visNE = new PositionDTO(0.0, 0.0); + this.visSW = new DegreePosition(0.0, 0.0); + this.visNE = new DegreePosition(0.0, 0.0); this.field = field; this.w = field.length; @@ -94,26 +95,25 @@ public class RectField implements VectorField { public void prevStep() { } - public PositionDTO getRandomPosition() { + public Position getRandomPosition() { double rndY = Math.random(); double rndX = Math.random(); - PositionDTO result = new PositionDTO(); - result.latDeg = rndY * this.visSW.latDeg + (1 - rndY) * this.visNE.latDeg; - result.lngDeg = rndX * this.visSW.lngDeg + (1 - rndX) * this.visNE.lngDeg; - return result; + double latDeg = rndY * this.visSW.getLatDeg() + (1 - rndY) * this.visNE.getLatDeg(); + double lngDeg = rndX * this.visSW.getLngDeg() + (1 - rndX) * this.visNE.getLngDeg(); + return new DegreePosition(latDeg, lngDeg); } - public boolean inBounds(PositionDTO p) { - return p.lngDeg >= this.x0 && p.lngDeg < this.x1 && p.latDeg >= this.y0 && p.latDeg < this.y1; + public boolean inBounds(Position p) { + return p.getLngDeg() >= this.x0 && p.getLngDeg() < this.x1 && p.getLatDeg() >= this.y0 && p.getLatDeg() < this.y1; } - public Vector interpolate(PositionDTO p) { - int na = (int)Math.floor(p.lngDeg); - int nb = (int)Math.floor(p.latDeg); - int ma = (int)Math.ceil(p.lngDeg); - int mb = (int)Math.ceil(p.latDeg); - double fa = p.lngDeg - na; - double fb = p.latDeg - nb; + public Vector interpolate(Position p) { + int na = (int)Math.floor(p.getLngDeg()); + int nb = (int)Math.floor(p.getLatDeg()); + int ma = (int)Math.ceil(p.getLngDeg()); + int mb = (int)Math.ceil(p.getLatDeg()); + double fa = p.getLngDeg() - na; + double fb = p.getLatDeg() - nb; Vector result = new Vector(); result.x = this.field[na][nb].x * (1 - fa) * (1 - fb) + this.field[ma][nb].x * fa * (1 - fb) + this.field[na][mb].x * (1 - fa) * fb + this.field[ma][mb].x * fa * fb; @@ -123,13 +123,13 @@ public class RectField implements VectorField { } - public Vector getVector(PositionDTO p) { - PositionDTO q = new PositionDTO(); - q.lngDeg = (this.w - 1 - 1e-6) * (p.lngDeg - this.x0) / (this.x1 - this.x0); - q.latDeg = (this.h - 1 - 1e-6) * (p.latDeg - this.y0) / (this.y1 - this.y0); - if ((q.lngDeg < 0)||(q.lngDeg > (this.w-1))||(q.latDeg < 0)||(q.latDeg > (this.h-1))) { + public Vector getVector(Position p) { + double lngDeg = (this.w - 1 - 1e-6) * (p.getLngDeg() - this.x0) / (this.x1 - this.x0); + double latDeg = (this.h - 1 - 1e-6) * (p.getLatDeg() - this.y0) / (this.y1 - this.y0); + if ((lngDeg < 0)||(lngDeg > (this.w-1))||(latDeg < 0)||(latDeg > (this.h-1))) { return null; } else { + Position q = new DegreePosition(latDeg, lngDeg); return this.interpolate(q); } } @@ -146,7 +146,7 @@ public class RectField implements VectorField { return 0.9 * Math.pow(1.7, Math.min(1.0, 6.0 - zoomLevel)); } - public double particleWeight(PositionDTO p, Vector v) { + public double particleWeight(Position p, Vector v) { return 1.0 - v.length() / this.maxLength; } @@ -165,19 +165,19 @@ public class RectField implements VectorField { return 1.0; } - public PositionDTO getFieldNE() { - return new PositionDTO(Math.max(this.y0, this.y1), Math.max(this.x0, this.x1)); + public Position getFieldNE() { + return new DegreePosition(Math.max(this.y0, this.y1), Math.max(this.x0, this.x1)); } - public PositionDTO getFieldSW() { - return new PositionDTO(Math.min(this.y0, this.y1), Math.min(this.x0, this.x1)); + public Position getFieldSW() { + return new DegreePosition(Math.min(this.y0, this.y1), Math.min(this.x0, this.x1)); } - public void setVisNE(PositionDTO visNE) { + public void setVisNE(Position visNE) { this.visNE = visNE; } - public void setVisSW(PositionDTO visSW) { + public void setVisSW(Position visSW) { this.visSW = visSW; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java index 9d6d9d5f25f..1c1366ba1b6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/SimulatorField.java @@ -4,7 +4,8 @@ import java.util.List; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONParser; -import com.sap.sailing.domain.common.dto.PositionDTO; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; import com.sap.sailing.gwt.ui.shared.WindFieldGenParamsDTO; @@ -13,8 +14,8 @@ public class SimulatorField implements VectorField { private boolean swarmDebug = false; - private PositionDTO rcStart; - private PositionDTO rcEnd; + private Position rcStart; + private Position rcEnd; private int resY; private int resX; @@ -25,9 +26,9 @@ public class SimulatorField implements VectorField { private double bdXi; private double bdPhi; - private PositionDTO bdA; - private PositionDTO bdB; - private PositionDTO bdC; + private Position bdA; + private Position bdB; + private Position bdC; private double xScale; @@ -36,17 +37,17 @@ public class SimulatorField implements VectorField { private double y0; private double y1; - public PositionDTO visSW; - public PositionDTO visNE; + public Position visSW; + public Position visNE; private double maxLength; private double particleFactor; private double lngScale; - private PositionDTO nvY; - private PositionDTO nvX; - private PositionDTO gvX; + private Position nvY; + private Position nvX; + private Position gvX; private double[][][] data; private int step; @@ -58,8 +59,8 @@ public class SimulatorField implements VectorField { String parseString = windData.windDataJSON.substring(18, windData.windDataJSON.length()-1) + "}"; JSONObject baseData = JSONParser.parseLenient(parseString).isObject(); - this.rcStart = new PositionDTO(baseData.get("rcStart").isObject().get("lat").isNumber().doubleValue(), baseData.get("rcStart").isObject().get("lng").isNumber().doubleValue()); - this.rcEnd = new PositionDTO(baseData.get("rcEnd").isObject().get("lat").isNumber().doubleValue(), baseData.get("rcEnd").isObject().get("lng").isNumber().doubleValue()); + this.rcStart = new DegreePosition(baseData.get("rcStart").isObject().get("lat").isNumber().doubleValue(), baseData.get("rcStart").isObject().get("lng").isNumber().doubleValue()); + this.rcEnd = new DegreePosition(baseData.get("rcEnd").isObject().get("lat").isNumber().doubleValue(), baseData.get("rcEnd").isObject().get("lng").isNumber().doubleValue()); this.resY = (int)baseData.get("resY").isNumber().doubleValue(); this.resX = (int)baseData.get("resX").isNumber().doubleValue(); @@ -69,8 +70,8 @@ public class SimulatorField implements VectorField { this.bdXi = (this.borderY + 0.5) / (this.resY - 1); this.bdPhi = 1.0 + 2*this.bdXi; - this.bdA = new PositionDTO(this.rcEnd.latDeg+(this.rcEnd.latDeg-this.rcStart.latDeg)*this.bdXi, this.rcEnd.lngDeg+(this.rcEnd.lngDeg-this.rcStart.lngDeg)*this.bdXi); - this.bdB = new PositionDTO((this.rcStart.latDeg-this.rcEnd.latDeg)*this.bdPhi, (this.rcStart.lngDeg-this.rcEnd.lngDeg)*this.bdPhi); + this.bdA = new DegreePosition(this.rcEnd.getLatDeg()+(this.rcEnd.getLatDeg()-this.rcStart.getLatDeg())*this.bdXi, this.rcEnd.getLngDeg()+(this.rcEnd.getLngDeg()-this.rcStart.getLngDeg())*this.bdXi); + this.bdB = new DegreePosition((this.rcStart.getLatDeg()-this.rcEnd.getLatDeg())*this.bdPhi, (this.rcStart.getLngDeg()-this.rcEnd.getLngDeg())*this.bdPhi); this.xScale = baseData.get("xScale").isNumber().doubleValue(); @@ -79,8 +80,8 @@ public class SimulatorField implements VectorField { this.y0 = baseData.get("boundsSW").isObject().get("lat").isNumber().doubleValue(); this.y1 = baseData.get("boundsNE").isObject().get("lat").isNumber().doubleValue(); - this.visSW = new PositionDTO(0.0, 0.0); - this.visNE = new PositionDTO(0.0, 0.0); + this.visSW = new DegreePosition(0.0, 0.0); + this.visNE = new DegreePosition(0.0, 0.0); List gridData = windData.getMatrix(); @@ -111,31 +112,32 @@ public class SimulatorField implements VectorField { this.particleFactor = 2.0; - double latAvg = (this.rcEnd.latDeg + this.rcStart.latDeg) / 2.; + double latAvg = (this.rcEnd.getLatDeg() + this.rcStart.getLatDeg()) / 2.; this.lngScale = Math.cos(latAvg * Math.PI / 180.0); - double difLat = this.rcEnd.latDeg - this.rcStart.latDeg; - double difLng = (this.rcEnd.lngDeg - this.rcStart.lngDeg) * this.lngScale; + double difLat = this.rcEnd.getLatDeg() - this.rcStart.getLatDeg(); + double difLng = (this.rcEnd.getLngDeg() - this.rcStart.getLngDeg()) * this.lngScale; double difLen = Math.sqrt(difLat*difLat + difLng*difLng); - this.nvY = new PositionDTO(difLat/difLen/difLen*(this.resY-1), difLng/difLen/difLen*(this.resY-1)); + this.nvY = new DegreePosition(difLat/difLen/difLen*(this.resY-1), difLng/difLen/difLen*(this.resY-1)); double nrmLat = -difLng/difLen; double nrmLng = difLat/difLen; - this.nvX = new PositionDTO( nrmLat/this.xScale/difLen*(this.resX-1), nrmLng/this.xScale/difLen*(this.resX-1) ); - this.gvX = new PositionDTO( nrmLat*this.xScale*difLen, nrmLng/this.lngScale*this.xScale*difLen ); - this.bdC = new PositionDTO( this.gvX.latDeg*(this.resX+2*this.borderX-1)/(this.resX-1), this.gvX.lngDeg*(this.resX+2*this.borderX-1)/(this.resX-1) ); + this.nvX = new DegreePosition( nrmLat/this.xScale/difLen*(this.resX-1), nrmLng/this.xScale/difLen*(this.resX-1) ); + this.gvX = new DegreePosition( nrmLat*this.xScale*difLen, nrmLng/this.lngScale*this.xScale*difLen ); + this.bdC = new DegreePosition( this.gvX.getLatDeg()*(this.resX+2*this.borderX-1)/(this.resX-1), this.gvX.getLngDeg()*(this.resX+2*this.borderX-1)/(this.resX-1) ); } public static native void console(String msg) /*-{ console.log(msg); }-*/; - public PositionDTO getRandomPosition() { + public Position getRandomPosition() { double rndY = Math.random(); double rndX = Math.random() - 0.5; - PositionDTO result = new PositionDTO(); - result.latDeg = this.bdA.latDeg + rndY * this.bdB.latDeg + rndX * this.bdC.latDeg; - result.lngDeg = this.bdA.lngDeg + rndY * this.bdB.lngDeg + rndX * this.bdC.lngDeg; + + double latDeg = this.bdA.getLatDeg() + rndY * this.bdB.getLatDeg() + rndX * this.bdC.getLatDeg(); + double lngDeg = this.bdA.getLngDeg() + rndY * this.bdB.getLngDeg() + rndX * this.bdC.getLngDeg(); + Position result = new DegreePosition(latDeg, lngDeg); if (swarmDebug&&(!this.inBounds(result))) { console("random-position: out of bounds"); @@ -145,14 +147,14 @@ public class SimulatorField implements VectorField { } - public boolean inBounds(PositionDTO p) { + public boolean inBounds(Position p) { Index idx = this.getIndex(p); boolean inBool = (idx.x >= 0) && (idx.x < (this.resX+2*this.borderX)) && (idx.y >= 0) && (idx.y < (this.resY+2*this.borderY)); return inBool; } - public Vector interpolate(PositionDTO p) { + public Vector interpolate(Position p) { Neighbors idx = this.getNeighbors(p); @@ -192,30 +194,30 @@ public class SimulatorField implements VectorField { } } - public Vector getVector(PositionDTO p) { + public Vector getVector(Position p) { return this.interpolate(p); } - public Index getIndex(PositionDTO p) { + public Index getIndex(Position p) { // calculate grid indexes - PositionDTO posR = new PositionDTO( p.latDeg - this.rcStart.latDeg, (p.lngDeg - this.rcStart.lngDeg) * this.lngScale ); + Position posR = new DegreePosition( p.getLatDeg() - this.rcStart.getLatDeg(), (p.getLngDeg() - this.rcStart.getLngDeg()) * this.lngScale ); // closest grid point - long yIdx = Math.round( posR.latDeg * this.nvY.latDeg + posR.lngDeg * this.nvY.lngDeg ) + this.borderY; - long xIdx = Math.round( posR.latDeg * this.nvX.latDeg + posR.lngDeg * this.nvX.lngDeg + (this.resX - 1) / 2. ) + this.borderX; + long yIdx = Math.round( posR.getLatDeg() * this.nvY.getLatDeg() + posR.getLngDeg() * this.nvY.getLngDeg() ) + this.borderY; + long xIdx = Math.round( posR.getLatDeg() * this.nvX.getLatDeg() + posR.getLngDeg() * this.nvX.getLngDeg() + (this.resX - 1) / 2. ) + this.borderX; return new Index( xIdx, yIdx ); } - public Neighbors getNeighbors(PositionDTO p) { + public Neighbors getNeighbors(Position p) { // calculate grid indexes - PositionDTO posR = new PositionDTO( p.latDeg - this.rcStart.latDeg, (p.lngDeg - this.rcStart.lngDeg) * this.lngScale ); + Position posR = new DegreePosition( p.getLatDeg() - this.rcStart.getLatDeg(), (p.getLngDeg() - this.rcStart.getLngDeg()) * this.lngScale ); // surrounding grid points - double yFlt = posR.latDeg * this.nvY.latDeg + posR.lngDeg * this.nvY.lngDeg + this.borderY; - double xFlt = posR.latDeg * this.nvX.latDeg + posR.lngDeg * this.nvX.lngDeg + (this.resX - 1) / 2. + this.borderX; + double yFlt = posR.getLatDeg() * this.nvY.getLatDeg() + posR.getLngDeg() * this.nvY.getLngDeg() + this.borderY; + double xFlt = posR.getLatDeg() * this.nvX.getLatDeg() + posR.getLngDeg() * this.nvX.getLngDeg() + (this.resX - 1) / 2. + this.borderX; double yBot = Math.floor( yFlt ); double xBot = Math.floor( xFlt ); double yTop = Math.ceil( yFlt ); @@ -252,7 +254,7 @@ public class SimulatorField implements VectorField { return 0.08 * Math.pow(1.6, Math.min(1.0, 6.0 - zoomLevel)); } - public double particleWeight(PositionDTO p, Vector v) { + public double particleWeight(Position p, Vector v) { return v.length() / this.maxLength + 0.1; } @@ -271,19 +273,19 @@ public class SimulatorField implements VectorField { return 1.0; } - public PositionDTO getFieldNE() { - return new PositionDTO(Math.max(this.y0, this.y1), Math.max(this.x0, this.x1)); + public Position getFieldNE() { + return new DegreePosition(Math.max(this.y0, this.y1), Math.max(this.x0, this.x1)); } - public PositionDTO getFieldSW() { - return new PositionDTO(Math.min(this.y0, this.y1), Math.min(this.x0, this.x1)); + public Position getFieldSW() { + return new DegreePosition(Math.min(this.y0, this.y1), Math.min(this.x0, this.x1)); } - public void setVisNE(PositionDTO visNE) { + public void setVisNE(Position visNE) { this.visNE = visNE; } - public void setVisSW(PositionDTO visSW) { + public void setVisSW(Position visSW) { this.visSW = visSW; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java index 3c0fd653943..5bd391bcb49 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/Swarm.java @@ -7,7 +7,8 @@ import com.google.gwt.canvas.dom.client.Context2d; import com.google.gwt.core.client.GWT; import com.google.gwt.maps.client.MapWidget; import com.google.gwt.user.client.Timer; -import com.sap.sailing.domain.common.dto.PositionDTO; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.gwt.ui.shared.WindFieldDTO; import com.sap.sailing.gwt.ui.simulator.WindStreamletsCanvasOverlay; import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; @@ -27,8 +28,8 @@ public class Swarm { private boolean swarmOffScreen = false; private int swarmPause = 0; private boolean swarmContinue = true; - private PositionDTO boundsNE; - private PositionDTO boundsSW; + private Position boundsNE; + private Position boundsSW; public Swarm(FullCanvasOverlay fullcanvas, MapWidget map) { this.fullcanvas = fullcanvas; @@ -129,11 +130,11 @@ public class Swarm { public void updateBounds() { - PositionDTO mapNE = new PositionDTO(map.getBounds().getNorthEast().getLatitude(), map.getBounds().getNorthEast().getLongitude()); - PositionDTO mapSW = new PositionDTO(map.getBounds().getSouthWest().getLatitude(), map.getBounds().getSouthWest().getLongitude()); + Position mapNE = new DegreePosition(map.getBounds().getNorthEast().getLatitude(), map.getBounds().getNorthEast().getLongitude()); + Position mapSW = new DegreePosition(map.getBounds().getSouthWest().getLatitude(), map.getBounds().getSouthWest().getLongitude()); - PositionDTO fieldNE = this.field.getFieldNE(); - PositionDTO fieldSW = this.field.getFieldSW(); + Position fieldNE = this.field.getFieldNE(); + Position fieldSW = this.field.getFieldSW(); Vector visibleNE = this.isVisible(fieldNE); Vector visibleSW = this.isVisible(fieldSW); @@ -155,9 +156,9 @@ public class Swarm { if ((!useBoundsNorth)&&(!useBoundsEast)) { this.boundsNE = mapNE; } else if (!useBoundsNorth) { - this.boundsNE = new PositionDTO(mapNE.latDeg, fieldNE.lngDeg); + this.boundsNE = new DegreePosition(mapNE.getLatDeg(), fieldNE.getLngDeg()); } else if (!useBoundsEast) { - this.boundsNE = new PositionDTO(fieldNE.latDeg, mapNE.lngDeg); + this.boundsNE = new DegreePosition(fieldNE.getLatDeg(), mapNE.getLngDeg()); } else { this.boundsNE = fieldNE; } @@ -165,9 +166,9 @@ public class Swarm { if ((!useBoundsSouth)&&(!useBoundsWest)) { this.boundsSW = mapSW; } else if (!useBoundsSouth) { - this.boundsSW = new PositionDTO(mapSW.latDeg, fieldSW.lngDeg); + this.boundsSW = new DegreePosition(mapSW.getLatDeg(), fieldSW.getLngDeg()); } else if (!useBoundsWest) { - this.boundsSW = new PositionDTO(fieldSW.latDeg, mapSW.lngDeg); + this.boundsSW = new DegreePosition(fieldSW.getLatDeg(), mapSW.getLngDeg()); } else { this.boundsSW = fieldSW; } @@ -187,7 +188,7 @@ public class Swarm { //console("#particles: "+this.nParticles + " at " + (boundsWidthpx) +"x" + (boundsHeightpx) + "px (" + (boundsWidthpx * boundsHeightpx) + " pixels)"); }; - public Vector isVisible(PositionDTO pos) { + public Vector isVisible(Position pos) { // test for visibility of swarm Vector proj = this.projection.latlng2pixel(pos); @@ -263,8 +264,9 @@ public class Swarm { for(int idx=0; idx 0) && (particle.v != null)) { - particle.pos.latDeg = particle.pos.latDeg + speed*particle.v.y; - particle.pos.lngDeg = particle.pos.lngDeg + speed*particle.v.x; + double latDeg = particle.pos.getLatDeg() + speed*particle.v.y; + double lngDeg = particle.pos.getLngDeg() + speed*particle.v.x; + particle.pos = new DegreePosition(latDeg, lngDeg); double s = particle.v.length() / field.getMaxLength(); particle.alpha = (int)Math.min(255, 90 + Math.round(350 * s)); particle.age--; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java index 5e106988a99..dbd0c28c896 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/streamlets/VectorField.java @@ -1,35 +1,35 @@ package com.sap.sailing.gwt.ui.simulator.streamlets; -import com.sap.sailing.domain.common.dto.PositionDTO; +import com.sap.sailing.domain.common.Position; public interface VectorField { - public PositionDTO getRandomPosition(); + public Position getRandomPosition(); - public boolean inBounds(PositionDTO p); + public boolean inBounds(Position p); - public Vector interpolate(PositionDTO p); + public Vector interpolate(Position p); - public Vector getVector(PositionDTO p); + public Vector getVector(Position p); public double getMaxLength(); public double motionScale(int zoomLevel); - public double particleWeight(PositionDTO p, Vector v); + public double particleWeight(Position p, Vector v); public String[] getColors(); public double lineWidth(int alpha); - public PositionDTO getFieldNE(); + public Position getFieldNE(); - public PositionDTO getFieldSW(); + public Position getFieldSW(); - public void setVisNE(PositionDTO visNE); + public void setVisNE(Position visNE); - public void setVisSW(PositionDTO visSW); + public void setVisSW(Position visSW); public double getParticleFactor(); From 390b0b5bce580e72177e488f0c638a9db3f0e658 Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Tue, 13 May 2014 17:27:47 +0200 Subject: [PATCH 38/66] Making sure that the amazon launch config is put at the right place --- configuration/buildAndUpdateProduct.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configuration/buildAndUpdateProduct.sh b/configuration/buildAndUpdateProduct.sh index 528d6230e88..7ab8ddf0e8f 100755 --- a/configuration/buildAndUpdateProduct.sh +++ b/configuration/buildAndUpdateProduct.sh @@ -242,6 +242,7 @@ if [[ "$@" == "release" ]]; then SIMPLE_VERSION_INFO="$OSGI_BUNDLE_NAME-$HEAD_DATE" fi + mkdir $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO echo " MONGODB_NAME=myspecificevent REPLICATION_CHANNEL=myspecificevent @@ -249,9 +250,8 @@ if [[ "$@" == "release" ]]; then SERVER_NAME=LIVE USE_ENVIRONMENT=live-server INSTALL_FROM_RELEASE=$SIMPLE_VERSION_INFO - " >> $ACDIR/amazon-launch-config.txt + " >> $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO/amazon-launch-config.txt - mkdir $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO `which tar` cvzf $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO/$SIMPLE_VERSION_INFO.tar.gz * cp $ACDIR/env.sh $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO cp $PROJECT_HOME/build/release-notes.txt $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO From a282cfa1b0fda8682bc8db257f5bf6d5f8f725ee Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 13 May 2014 20:31:28 +0200 Subject: [PATCH 39/66] fix gwt compile issue due to missing parameter in competitorRegistrationsMissingProceed; --- .../com/sap/sailing/gwt/ui/client/StringMessages_ru.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index b87f4eed600..c4652bbe8d5 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -957,7 +957,7 @@ importWindTooltip=Снятие флажка с этой опции значит authorName=Имя автора authorPriority=Приоритет автора removeDenotation=todo -competitorRegistrationsMissingProceed=todo +competitorRegistrationsMissingProceed=todo{0} pleaseSelectAControlPoint=todo twoMarkControlPoint=todo registeredCompetitors=todo From c960081579b343d925ee0f0aadc90e2e515cfaef Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 13 May 2014 23:51:47 +0200 Subject: [PATCH 40/66] Reduced the number of races tested for markpassings to reduce build time. --- .../FlorianopolisMarkPassingTest.java | 130 +++++++----------- .../markpassing/KW470MarkPassingTest.java | 20 +-- .../OBMR2012VRMarkPassingTest.java | 82 +++++------ 3 files changed, 95 insertions(+), 137 deletions(-) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/FlorianopolisMarkPassingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/FlorianopolisMarkPassingTest.java index e3f1c91081a..54e73723ee0 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/FlorianopolisMarkPassingTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/FlorianopolisMarkPassingTest.java @@ -13,16 +13,6 @@ public class FlorianopolisMarkPassingTest extends AbstractMarkPassingTest { super(); } - @Test - public void testRace1() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("1"); - } - - @Test - public void testRace2() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("2"); - } - @Test public void testRace3() throws IOException, InterruptedException, URISyntaxException, ParseException { testRace("3"); @@ -33,75 +23,57 @@ public class FlorianopolisMarkPassingTest extends AbstractMarkPassingTest { testRace("4"); } - @Test - public void testRace5() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("5"); - } - - @Test - public void testRace6() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("6"); - } - - @Test - public void testRace7() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("7"); - } - - @Test - public void testRace8() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("8"); - } - - @Test - public void testRace10() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("10"); - } - - @Test - public void testRace11() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("11"); - } - - @Test - public void testRace12() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("12"); - } - - @Test - public void testRace13() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("13"); - } - - @Test - public void testRace14() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("14"); - } - - @Test - public void testRace16() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("16"); - } - - @Test - public void testRace17() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("17"); - } - - @Test - public void testRace19() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("19"); - } - - @Test - public void testRace22() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("22"); - } - - @Test - public void testRace24() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("24"); - } + /* + * Test ignored on master branch to quicken the hudson build. + * + * @Test public void testRace1() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("1"); } + * + * @Test public void testRace2() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("2"); } + * + * @Test public void testRace5() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("5"); } + * + * @Test public void testRace6() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("6"); } + * + * @Test public void testRace7() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("7"); } + * + * @Test public void testRace8() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("8"); } + * + * @Test public void testRace10() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("10"); } + * + * @Test public void testRace11() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("11"); } + * + * @Test public void testRace12() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("12"); } + * + * @Test public void testRace13() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("13"); } + * + * @Test public void testRace14() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("14"); } + * + * @Test public void testRace16() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("16"); } + * + * @Test public void testRace17() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("17"); } + * + * @Test public void testRace19() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("19"); } + * + * @Test public void testRace22() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("22"); } + * + * @Test public void testRace24() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("24"); } + */ /* * Alinghi acting wierd diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/KW470MarkPassingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/KW470MarkPassingTest.java index 976f9709170..1a710b8e9f4 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/KW470MarkPassingTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/KW470MarkPassingTest.java @@ -17,16 +17,16 @@ public class KW470MarkPassingTest extends AbstractMarkPassingTest { public void testRace4() throws IOException, InterruptedException, URISyntaxException, ParseException { testRace("4"); } - - @Test - public void testRace5() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("5"); - } - - @Test - public void testRace6() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("6"); - } + + /* + * Ignored on master branch + * + * @Test public void testRace5() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("5"); } + * + * @Test public void testRace6() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("6"); } + */ @Override protected String getFileName() { diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/OBMR2012VRMarkPassingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/OBMR2012VRMarkPassingTest.java index 8e82227a25b..6fb6a2763f0 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/OBMR2012VRMarkPassingTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/markpassing/OBMR2012VRMarkPassingTest.java @@ -9,7 +9,7 @@ import org.junit.Test; public class OBMR2012VRMarkPassingTest extends AbstractMarkPassingTest { - public OBMR2012VRMarkPassingTest() throws MalformedURLException, URISyntaxException { + public OBMR2012VRMarkPassingTest() throws MalformedURLException, URISyntaxException { super(); } @@ -17,57 +17,43 @@ public class OBMR2012VRMarkPassingTest extends AbstractMarkPassingTest { public void testRaceFr11() throws IOException, InterruptedException, URISyntaxException, ParseException { testRace("Fr_Fleet_1_1"); } - + @Test public void testRaceFr12() throws IOException, InterruptedException, URISyntaxException, ParseException { testRace("Fr_Fleet_1_2"); } - - @Test - public void testRaceFr13() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("Fr_Fleet_1_3"); - } - - @Test - public void testRaceFr21() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("Fr_Fleet_2_1"); - } - - @Test - public void testRaceFr22() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("Fr_Fleet_2_2"); - } - - @Test - public void testRaceFr23() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("Fr_Fleet_2_3"); - } - - @Test - public void testRaceSaVm11() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("Sa-Vm_Fleet_1_1"); - } - - @Test - public void testRaceSaVm12() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("Sa-Vm_Fleet_1_2"); - } - - @Test - public void testRaceSaVm13() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("Sa-Vm_Fleet_1_3"); - } - - @Test - public void testRaceSaVm21() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("Sa-Vm_Fleet_2_1"); - } - - @Test - public void testRaceSaVm22() throws IOException, InterruptedException, URISyntaxException, ParseException { - testRace("Sa-Vm_Fleet_2_2"); - } - + + /* + * Ignored on master branch. + * + * @Test public void testRaceFr13() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("Fr_Fleet_1_3"); } + * + * @Test public void testRaceFr21() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("Fr_Fleet_2_1"); } + * + * @Test public void testRaceFr22() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("Fr_Fleet_2_2"); } + * + * @Test public void testRaceFr23() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("Fr_Fleet_2_3"); } + * + * @Test public void testRaceSaVm11() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("Sa-Vm_Fleet_1_1"); } + * + * @Test public void testRaceSaVm12() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("Sa-Vm_Fleet_1_2"); } + * + * @Test public void testRaceSaVm13() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("Sa-Vm_Fleet_1_3"); } + * + * @Test public void testRaceSaVm21() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("Sa-Vm_Fleet_2_1"); } + * + * @Test public void testRaceSaVm22() throws IOException, InterruptedException, URISyntaxException, ParseException { + * testRace("Sa-Vm_Fleet_2_2"); } + */ + @Override protected String getExpectedEventName() { return "OBMR 2012"; From f75031deb701e27ec9bbcf69aa8c1c34fda13bfe Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Wed, 14 May 2014 10:14:26 +0200 Subject: [PATCH 41/66] Revert "fix gwt compile issue due to missing parameter in competitorRegistrationsMissingProceed;" This reverts commit a282cfa1b0fda8682bc8db257f5bf6d5f8f725ee. --- .../com/sap/sailing/gwt/ui/client/StringMessages_ru.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index c4652bbe8d5..b87f4eed600 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -957,7 +957,7 @@ importWindTooltip=Снятие флажка с этой опции значит authorName=Имя автора authorPriority=Приоритет автора removeDenotation=todo -competitorRegistrationsMissingProceed=todo{0} +competitorRegistrationsMissingProceed=todo pleaseSelectAControlPoint=todo twoMarkControlPoint=todo registeredCompetitors=todo From 3c9b493687a7601cf578a717a6377df8445bfb70 Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Wed, 14 May 2014 11:01:09 +0200 Subject: [PATCH 42/66] Added information about how to translate new properties --- .../com/sap/sailing/gwt/ui/client/StringMessages_ru.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index 69b5f3c9bd1..6e33a0cced0 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -1,3 +1,4 @@ +# If you want new properties translated then sent this over GMR (Mark Angell ) helloWorld=Привет Мир kielWeel2011=Кильская Неделя 2011 stgAccount=Счет STG From 3928e2f9ce462e07335f034f5171ed7be78c7be5 Mon Sep 17 00:00:00 2001 From: Simon Pamies Date: Wed, 14 May 2014 12:41:03 +0200 Subject: [PATCH 43/66] Changed spacing for amazon default config and changed server name to specific event --- configuration/buildAndUpdateProduct.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/configuration/buildAndUpdateProduct.sh b/configuration/buildAndUpdateProduct.sh index 7ab8ddf0e8f..edf4b374781 100755 --- a/configuration/buildAndUpdateProduct.sh +++ b/configuration/buildAndUpdateProduct.sh @@ -244,12 +244,12 @@ if [[ "$@" == "release" ]]; then mkdir $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO echo " - MONGODB_NAME=myspecificevent - REPLICATION_CHANNEL=myspecificevent - SERVER_STARTUP_NOTIFY=simon.marcel.pamies@sap.com - SERVER_NAME=LIVE - USE_ENVIRONMENT=live-server - INSTALL_FROM_RELEASE=$SIMPLE_VERSION_INFO +MONGODB_NAME=myspecificevent +REPLICATION_CHANNEL=myspecificevent +SERVER_STARTUP_NOTIFY=simon.marcel.pamies@sap.com +SERVER_NAME=MYSPECIFICEVENT +USE_ENVIRONMENT=live-server +INSTALL_FROM_RELEASE=$SIMPLE_VERSION_INFO " >> $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO/amazon-launch-config.txt `which tar` cvzf $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO/$SIMPLE_VERSION_INFO.tar.gz * From 2ea2457409c178059e722e4a2ba109eb546377cb Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 14 May 2014 13:11:59 +0200 Subject: [PATCH 44/66] added net.sf.marineapi to Jetty on 8889 launch config with Proxy --- .../SailingServer (Proxy, Jetty on 8889).launch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch index 2ccb1f54ac0..c0341a03ab9 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch @@ -26,6 +26,6 @@ - + From 958d7b15e2cd025042b60cc4251441337f5915df Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 14 May 2014 14:26:57 +0200 Subject: [PATCH 45/66] fixing bug 1935 by initializing the RaceLogInformation after deserializing RegattaImpl and FlexibleLeaderboardImpl objects; --- .../sailing/domain/base/impl/AbstractRaceColumn.java | 1 - .../sap/sailing/domain/base/impl/RegattaImpl.java | 12 +++++++++++- .../leaderboard/impl/FlexibleLeaderboardImpl.java | 8 +++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java index 901546fae10..d5a6ad339ca 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java @@ -39,7 +39,6 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem this.trackedRaces = new TrackedRaces(); this.raceIdentifiers = new HashMap(); this.raceLogs = new HashMap(); - } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java index 614d517328a..02127365bd4 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java @@ -34,6 +34,7 @@ import com.sap.sailing.domain.leaderboard.ResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.racelog.RaceLogEvent; import com.sap.sailing.domain.racelog.RaceLogIdentifier; +import com.sap.sailing.domain.racelog.RaceLogInformation; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.RaceLogInformationImpl; @@ -159,11 +160,20 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene /** * When de-serializing, a possibly remote {@link #raceLogStore} is ignored because it is transient. Instead, an - * {@link EmptyRaceLogStore} is used for the de-serialized instance. + * {@link EmptyRaceLogStore} is used for the de-serialized instance. A new {@link RaceLogInformation} is assembled + * for this empty race log and applied to all columns. */ private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { ois.defaultReadObject(); raceLogStore = EmptyRaceLogStore.INSTANCE; + for (Series series : getSeries()) { + if (series.getRaceColumns() != null) { + for (RaceColumnInSeries column : series.getRaceColumns()) { + column.setRaceLogInformation(new RaceLogInformationImpl(raceLogStore, + new RaceLogOnRegattaIdentifier(this, column.getName()))); + } + } + } regattaListeners = new HashSet(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java index a61fe685c5b..487998135e4 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java @@ -19,6 +19,7 @@ import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.FlexibleRaceColumn; import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; +import com.sap.sailing.domain.racelog.RaceLogInformation; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; import com.sap.sailing.domain.racelog.impl.RaceLogInformationImpl; @@ -67,11 +68,16 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements /** * Deserialization has to be maintained in lock-step with {@link #writeObject(ObjectOutputStream) serialization}. * When de-serializing, a possibly remote {@link #raceLogStore} is ignored because it is transient. Instead, an - * {@link EmptyRaceLogStore} is used for the de-serialized instance. + * {@link EmptyRaceLogStore} is used for the de-serialized instance. A new {@link RaceLogInformation} is + * assembled for this empty race log and applied to all columns. */ private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { ois.defaultReadObject(); raceLogStore = EmptyRaceLogStore.INSTANCE; + for (RaceColumn column : getRaceColumns()) { + column.setRaceLogInformation(new RaceLogInformationImpl(raceLogStore, new RaceLogOnLeaderboardIdentifier( + this, column.getName()))); + } } @Override From 40d57dbdda947609d11fc55336503ac3e988fcbc Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 14 May 2014 21:09:05 +0200 Subject: [PATCH 46/66] working on the completion of bug 1935 regarding the consistent use of locks instead of synchronized where possible --- .../sailing/domain/base/impl/VenueImpl.java | 26 +- .../server/impl/RacingEventServiceImpl.java | 304 +++++++++--------- 2 files changed, 174 insertions(+), 156 deletions(-) diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/VenueImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/VenueImpl.java index 68f879a9991..8fc9d75308d 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/VenueImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/VenueImpl.java @@ -6,6 +6,8 @@ import java.util.List; import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.Venue; +import com.sap.sailing.util.impl.LockUtil; +import com.sap.sailing.util.impl.NamedReentrantReadWriteLock; public class VenueImpl implements Venue { private static final long serialVersionUID = 6854152040737643290L; @@ -16,25 +18,43 @@ public class VenueImpl implements Venue { * "NATO alphabet" (Alpha, Bravo, Charlie, ...). */ private final List courseAreas; + + private final NamedReentrantReadWriteLock courseAreasLock; public VenueImpl(String name) { this.name = name; courseAreas = new ArrayList(); + courseAreasLock = new NamedReentrantReadWriteLock("Course Areas for venue "+name, /* fair */ false); } @Override public Iterable getCourseAreas() { - return Collections.unmodifiableList(courseAreas); + LockUtil.lockForRead(courseAreasLock); + try { + return Collections.unmodifiableList(courseAreas); + } finally { + LockUtil.unlockAfterRead(courseAreasLock); + } } @Override public void addCourseArea(CourseArea courseArea) { - courseAreas.add(courseArea); + LockUtil.lockForWrite(courseAreasLock); + try { + courseAreas.add(courseArea); + } finally { + LockUtil.unlockAfterWrite(courseAreasLock); + } } @Override public void removeCourseArea(CourseArea courseArea) { - courseAreas.remove(courseArea); + LockUtil.lockForWrite(courseAreasLock); + try { + courseAreas.remove(courseArea); + } finally { + LockUtil.unlockAfterWrite(courseAreasLock); + } } @Override diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index f58583bdb6a..855d4285471 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -222,9 +222,22 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport * Leaderboards managed by this racing event service */ private final ConcurrentHashMap leaderboardsByName; + + /** + * {@link #leaderboardsByName} is already a concurrent hash map; however, when renaming a leaderboard, this shall + * happen as an atomic transaction, not interruptible by other write accesses on the same map because otherwise + * assumptions made during the rename process wouldn't hold. See, in particular, + * {@link #renameLeaderboard(String, String)}. + */ + private final NamedReentrantReadWriteLock leaderboardsByNameLock; private final ConcurrentHashMap leaderboardGroupsByName; + /** + * See {@link #leaderboardsByNameLock} + */ + private final NamedReentrantReadWriteLock leaderboardGroupsByNameLock; + private final CompetitorStore competitorStore; private Set regattasObservedForDefaultLeaderboard = new HashSet(); @@ -365,7 +378,9 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport raceTrackersByID = new ConcurrentHashMap<>(); raceTrackersByIDLocks = new ConcurrentHashMap<>(); leaderboardGroupsByName = new ConcurrentHashMap<>(); + leaderboardGroupsByNameLock = new NamedReentrantReadWriteLock("leaderboardGroupsByName for "+this, /* fair */ false); leaderboardsByName = new ConcurrentHashMap(); + leaderboardsByNameLock = new NamedReentrantReadWriteLock("leaderboardsByName for "+this, /* fair */ false); operationExecutionListeners = new ConcurrentHashMap<>(); courseListeners = new ConcurrentHashMap<>(); persistentRegattasForRaceIDs = new ConcurrentHashMap<>(); @@ -398,29 +413,23 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public void clearState() throws Exception { - for(String leaderboardGroupName : this.leaderboardGroupsByName.keySet()) { + for (String leaderboardGroupName : this.leaderboardGroupsByName.keySet()) { removeLeaderboardGroup(leaderboardGroupName); } - - for(String leaderboardName : this.leaderboardsByName.keySet()) { + for (String leaderboardName : this.leaderboardsByName.keySet()) { removeLeaderboard(leaderboardName); } - for(Regatta regatta : this.regattasByName.values()) { stopTracking(regatta); removeRegatta(regatta); } - for(Event event : this.eventsById.values()) { removeEvent(event.getId()); } - for(MediaTrack mediaTrack : this.mediaLibrary.allTracks()) { mediaTrackDeleted(mediaTrack); } - this.competitorStore.clear(); - // Add one default leaderboard that aggregates all races currently tracked by this service. // This is more for debugging purposes than for anything else. addFlexibleLeaderboard(LeaderboardNameConstants.DEFAULT_LEADERBOARD_NAME, null, new int[] { 5, 8 }, @@ -458,10 +467,8 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport private void loadStoredEvents() { for (Event event : domainObjectFactory.loadAllEvents()) { - synchronized (eventsById) { - if (event.getId() != null) - eventsById.put(event.getId(), event); - } + if (event.getId() != null) + eventsById.put(event.getId(), event); } } @@ -485,21 +492,22 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport private void loadStoredDeviceConfigurations() { for (Entry entry : domainObjectFactory.loadAllDeviceConfigurations()) { - synchronized (configurationMap) { - configurationMap.put(entry.getKey(), entry.getValue()); - } + configurationMap.put(entry.getKey(), entry.getValue()); } } @Override public void addLeaderboard(Leaderboard leaderboard) { - synchronized (leaderboardsByName) { + LockUtil.lockForWrite(leaderboardsByNameLock); + try { leaderboardsByName.put(leaderboard.getName(), leaderboard); - // RaceColumns of RegattaLeaderboards are tracked via its Regatta! - if (leaderboard instanceof FlexibleLeaderboard) { - leaderboard.addRaceColumnListener(raceLogReplicator); - leaderboard.addRaceColumnListener(raceLogScoringReplicator); - } + } finally { + LockUtil.unlockAfterWrite(leaderboardsByNameLock); + } + // RaceColumns of RegattaLeaderboards are tracked via its Regatta! + if (leaderboard instanceof FlexibleLeaderboard) { + leaderboard.addRaceColumnListener(raceLogReplicator); + leaderboard.addRaceColumnListener(raceLogScoringReplicator); } } @@ -508,7 +516,12 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport // Loading all leaderboard groups and the contained leaderboards for (LeaderboardGroup leaderboardGroup : domainObjectFactory.getAllLeaderboardGroups(this, this)) { logger.info("loaded leaderboard group " + leaderboardGroup.getName() + " into " + this); - leaderboardGroupsByName.put(leaderboardGroup.getName(), leaderboardGroup); + LockUtil.lockForWrite(leaderboardGroupsByNameLock); + try { + leaderboardGroupsByName.put(leaderboardGroup.getName(), leaderboardGroup); + } finally { + LockUtil.unlockAfterWrite(leaderboardGroupsByNameLock); + } } // Loading the remaining leaderboards domainObjectFactory.getLeaderboardsNotInGroup(this, this); @@ -526,12 +539,10 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport new ThresholdBasedResultDiscardingRuleImpl(discardThresholds), scoringScheme, courseArea); result.setDisplayName(leaderboardDisplayName); - synchronized (leaderboardsByName) { - if (getLeaderboardByName(leaderboardName) != null) { - throw new IllegalArgumentException("Leaderboard with name " + leaderboardName + " already exists"); - } - addLeaderboard(result); + if (getLeaderboardByName(leaderboardName) != null) { + throw new IllegalArgumentException("Leaderboard with name " + leaderboardName + " already exists"); } + addLeaderboard(result); mongoObjectFactory.storeLeaderboard(result); return result; } @@ -558,13 +569,11 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport if (regatta != null) { result = new RegattaLeaderboardImpl(regatta, new ThresholdBasedResultDiscardingRuleImpl(discardThresholds)); result.setDisplayName(leaderboardDisplayName); - synchronized (leaderboardsByName) { - if (getLeaderboardByName(result.getName()) != null) { - throw new IllegalArgumentException("Leaderboard with name " + result.getName() - + " already exists in " + this); - } - addLeaderboard(result); + if (getLeaderboardByName(result.getName()) != null) { + throw new IllegalArgumentException("Leaderboard with name " + result.getName() + " already exists in " + + this); } + addLeaderboard(result); mongoObjectFactory.storeLeaderboard(result); } else { logger.warning("Cannot find regatta " + regattaIdentifier @@ -688,24 +697,30 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public void renameLeaderboard(String oldName, String newName) { - synchronized (leaderboardsByName) { - if (!leaderboardsByName.containsKey(oldName)) { + final Leaderboard toRename = leaderboardsByName.get(oldName); + LockUtil.lockForWrite(leaderboardsByNameLock); + try { + if (toRename == null) { throw new IllegalArgumentException("No leaderboard with name " + oldName + " found"); } if (leaderboardsByName.containsKey(newName)) { throw new IllegalArgumentException("Leaderboard with name " + newName + " already exists"); } - Leaderboard toRename = leaderboardsByName.get(oldName); if (toRename instanceof Renamable) { ((Renamable) toRename).setName(newName); leaderboardsByName.remove(oldName); leaderboardsByName.put(newName, toRename); - mongoObjectFactory.renameLeaderboard(oldName, newName); - syncGroupsAfterLeaderboardChange(toRename, true); } else { throw new IllegalArgumentException("Leaderboard with name " + newName + " is of type " + toRename.getClass().getSimpleName() + " and therefore cannot be renamed"); } + } finally { + LockUtil.unlockAfterWrite(leaderboardsByNameLock); + } + // don't need the lock anymore to update DB + if (toRename instanceof Renamable) { + mongoObjectFactory.renameLeaderboard(oldName, newName); + syncGroupsAfterLeaderboardChange(toRename, true); } } @@ -729,24 +744,22 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport */ private void syncGroupsAfterLeaderboardChange(Leaderboard updatedLeaderboard, boolean doDatabaseUpdate) { boolean groupNeedsUpdate = false; - synchronized (leaderboardGroupsByName) { - for (LeaderboardGroup leaderboardGroup : leaderboardGroupsByName.values()) { - for (Leaderboard leaderboard : leaderboardGroup.getLeaderboards()) { - if (leaderboard == updatedLeaderboard) { - int index = leaderboardGroup.getIndexOf(leaderboard); - leaderboardGroup.removeLeaderboard(leaderboard); - leaderboardGroup.addLeaderboardAt(updatedLeaderboard, index); - groupNeedsUpdate = true; - // TODO we assume that the leaderboard names are unique, so we can break the inner loop here - break; - } + for (LeaderboardGroup leaderboardGroup : leaderboardGroupsByName.values()) { + for (Leaderboard leaderboard : leaderboardGroup.getLeaderboards()) { + if (leaderboard == updatedLeaderboard) { + int index = leaderboardGroup.getIndexOf(leaderboard); + leaderboardGroup.removeLeaderboard(leaderboard); + leaderboardGroup.addLeaderboardAt(updatedLeaderboard, index); + groupNeedsUpdate = true; + // TODO we assume that the leaderboard names are unique, so we can break the inner loop here + break; } - - if (doDatabaseUpdate && groupNeedsUpdate) { - mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup); - } - groupNeedsUpdate = false; } + + if (doDatabaseUpdate && groupNeedsUpdate) { + mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup); + } + groupNeedsUpdate = false; } } @@ -763,8 +776,11 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport } private Leaderboard removeLeaderboardFromLeaderboardsByName(String leaderboardName) { - synchronized (leaderboardsByName) { + LockUtil.lockForWrite(leaderboardsByNameLock); + try { return leaderboardsByName.remove(leaderboardName); + } finally { + LockUtil.unlockAfterWrite(leaderboardsByNameLock); } } @@ -776,37 +792,31 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport */ private void syncGroupsAfterLeaderboardRemove(String removedLeaderboardName, boolean doDatabaseUpdate) { boolean groupNeedsUpdate = false; - synchronized (leaderboardGroupsByName) { - for (LeaderboardGroup leaderboardGroup : leaderboardGroupsByName.values()) { - for (Leaderboard leaderboard : leaderboardGroup.getLeaderboards()) { - if (leaderboard.getName().equals(removedLeaderboardName)) { - leaderboardGroup.removeLeaderboard(leaderboard); - groupNeedsUpdate = true; - // TODO we assume that the leaderboard names are unique, so we can break the inner loop here - break; - } + for (LeaderboardGroup leaderboardGroup : leaderboardGroupsByName.values()) { + for (Leaderboard leaderboard : leaderboardGroup.getLeaderboards()) { + if (leaderboard.getName().equals(removedLeaderboardName)) { + leaderboardGroup.removeLeaderboard(leaderboard); + groupNeedsUpdate = true; + // TODO we assume that the leaderboard names are unique, so we can break the inner loop here + break; } - - if (doDatabaseUpdate && groupNeedsUpdate) { - mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup); - } - groupNeedsUpdate = false; } + + if (doDatabaseUpdate && groupNeedsUpdate) { + mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup); + } + groupNeedsUpdate = false; } } @Override public Leaderboard getLeaderboardByName(String name) { - synchronized (leaderboardsByName) { - return leaderboardsByName.get(name); - } + return leaderboardsByName.get(name); } @Override public Map getLeaderboards() { - synchronized (leaderboardsByName) { - return Collections.unmodifiableMap(new HashMap(leaderboardsByName)); - } + return Collections.unmodifiableMap(new HashMap(leaderboardsByName)); } @Override @@ -955,7 +965,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport * need to pass the lock obtained from {@link #lockRaceTrackersById(Object)} because a competing thread * may already have removed the lock from the {@link #raceTrackersByIDLocks} map */ - private synchronized void unlockRaceTrackersById(Object trackerId, NamedReentrantReadWriteLock lock) { + private void unlockRaceTrackersById(Object trackerId, NamedReentrantReadWriteLock lock) { LockUtil.unlockAfterWrite(lock); synchronized (raceTrackersByIDLocks) { raceTrackersByIDLocks.remove(trackerId); @@ -1641,16 +1651,12 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public Map getLeaderboardGroups() { - synchronized (leaderboardGroupsByName) { - return Collections.unmodifiableMap(new HashMap(leaderboardGroupsByName)); - } + return Collections.unmodifiableMap(new HashMap(leaderboardGroupsByName)); } @Override public LeaderboardGroup getLeaderboardGroupByName(String groupName) { - synchronized (leaderboardGroupsByName) { - return leaderboardGroupsByName.get(groupName); - } + return leaderboardGroupsByName.get(groupName); } @Override @@ -1658,14 +1664,12 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport boolean displayGroupsInReverseOrder, List leaderboardNames, int[] overallLeaderboardDiscardThresholds, ScoringSchemeType overallLeaderboardScoringSchemeType) { ArrayList leaderboards = new ArrayList<>(); - synchronized (leaderboardsByName) { - for (String leaderboardName : leaderboardNames) { - Leaderboard leaderboard = leaderboardsByName.get(leaderboardName); - if (leaderboard == null) { - throw new IllegalArgumentException("No leaderboard with name " + leaderboardName + " found"); - } else { - leaderboards.add(leaderboard); - } + for (String leaderboardName : leaderboardNames) { + Leaderboard leaderboard = leaderboardsByName.get(leaderboardName); + if (leaderboard == null) { + throw new IllegalArgumentException("No leaderboard with name " + leaderboardName + " found"); + } else { + leaderboards.add(leaderboard); } } LeaderboardGroup result = new LeaderboardGroupImpl(groupName, description, displayGroupsInReverseOrder, @@ -1676,11 +1680,14 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport getBaseDomainFactory().createScoringScheme(overallLeaderboardScoringSchemeType), overallLeaderboardDiscardThresholds); } - synchronized (leaderboardGroupsByName) { + LockUtil.lockForWrite(leaderboardGroupsByNameLock); + try { if (leaderboardGroupsByName.containsKey(groupName)) { throw new IllegalArgumentException("Leaderboard group with name " + groupName + " already exists"); } leaderboardGroupsByName.put(groupName, result); + } finally { + LockUtil.unlockAfterWrite(leaderboardGroupsByNameLock); } mongoObjectFactory.storeLeaderboardGroup(result); return result; @@ -1689,8 +1696,11 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public void removeLeaderboardGroup(String groupName) { final LeaderboardGroup leaderboardGroup; - synchronized (leaderboardGroupsByName) { + LockUtil.lockForWrite(leaderboardGroupsByNameLock); + try { leaderboardGroup = leaderboardGroupsByName.remove(groupName); + } finally { + LockUtil.unlockAfterWrite(leaderboardGroupsByNameLock); } mongoObjectFactory.removeLeaderboardGroup(groupName); if (leaderboardGroup != null && leaderboardGroup.getOverallLeaderboard() != null) { @@ -1700,18 +1710,22 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public void renameLeaderboardGroup(String oldName, String newName) { - synchronized (leaderboardGroupsByName) { - if (!leaderboardGroupsByName.containsKey(oldName)) { + LockUtil.lockForWrite(leaderboardGroupsByNameLock); + try { + final LeaderboardGroup toRename = leaderboardGroupsByName.get(oldName); + if (toRename == null) { throw new IllegalArgumentException("No leaderboard group with name " + oldName + " found"); } if (leaderboardGroupsByName.containsKey(newName)) { throw new IllegalArgumentException("Leaderboard group with name " + newName + " already exists"); } - LeaderboardGroup toRename = leaderboardGroupsByName.remove(oldName); + leaderboardGroupsByName.remove(oldName); toRename.setName(newName); leaderboardGroupsByName.put(newName, toRename); - mongoObjectFactory.renameLeaderboardGroup(oldName, newName); + } finally { + LockUtil.unlockAfterWrite(leaderboardGroupsByNameLock); } + mongoObjectFactory.renameLeaderboardGroup(oldName, newName); } @Override @@ -1892,7 +1906,12 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport logger.info("Clearing all data structures..."); regattaTrackingCache.clear(); leaderboardGroupsByName.clear(); - leaderboardsByName.clear(); + LockUtil.lockForWrite(leaderboardsByNameLock); + try { + leaderboardsByName.clear(); + } finally { + LockUtil.unlockAfterWrite(leaderboardsByNameLock); + } eventsById.clear(); mediaLibrary.clear(); competitorStore.clear(); @@ -1977,23 +1996,19 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public Event addEvent(String eventName, TimePoint startDate, TimePoint endDate, String venue, boolean isPublic, UUID id) { - synchronized (eventsById) { - Event result = createEventWithoutReplication(eventName, startDate, endDate, venue, isPublic, id); - replicate(new CreateEvent(eventName, startDate, endDate, venue, isPublic, id)); - return result; - } + Event result = createEventWithoutReplication(eventName, startDate, endDate, venue, isPublic, id); + replicate(new CreateEvent(eventName, startDate, endDate, venue, isPublic, id)); + return result; } @Override public Event createEventWithoutReplication(String eventName, TimePoint startDate, TimePoint endDate, String venue, boolean isPublic, UUID id) { Event result = new EventImpl(eventName, startDate, endDate, venue, isPublic, id); - synchronized (eventsById) { - if (eventsById.containsKey(result.getId())) { - throw new IllegalArgumentException("Event with ID " + result.getId() - + " already exists which is pretty surprising..."); - } - eventsById.put(result.getId(), result); + if (eventsById.containsKey(result.getId())) { + throw new IllegalArgumentException("Event with ID " + result.getId() + + " already exists which is pretty surprising..."); } + eventsById.put(result.getId(), result); mongoObjectFactory.storeEvent(result); return result; } @@ -2001,36 +2016,29 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public void updateEvent(UUID id, String eventName, TimePoint startDate, TimePoint endDate, String venueName, boolean isPublic, List regattaNames) { - synchronized (eventsById) { - if (!eventsById.containsKey(id)) { - throw new IllegalArgumentException("Sailing event with ID " + id + " does not exist."); - } - Event event = eventsById.get(id); - event.setName(eventName); - event.setStartDate(startDate); - event.setEndDate(endDate); - event.setPublic(isPublic); - event.getVenue().setName(venueName); - - // TODO need to update regattas if they are once linked to event objects - mongoObjectFactory.storeEvent(event); - - replicate(new UpdateEvent(id, eventName, startDate, endDate, venueName, isPublic, regattaNames)); + final Event event = eventsById.get(id); + if (event == null) { + throw new IllegalArgumentException("Sailing event with ID " + id + " does not exist."); } + event.setName(eventName); + event.setStartDate(startDate); + event.setEndDate(endDate); + event.setPublic(isPublic); + event.getVenue().setName(venueName); + // TODO need to update regattas if they are once linked to event objects + mongoObjectFactory.storeEvent(event); + replicate(new UpdateEvent(id, eventName, startDate, endDate, venueName, isPublic, regattaNames)); } @Override public void renameEvent(UUID id, String newName) { - synchronized (eventsById) { - if (!eventsById.containsKey(id)) { - throw new IllegalArgumentException("No sailing event with ID " + id + " found."); - } - Event toRename = eventsById.get(id); - toRename.setName(newName); - mongoObjectFactory.renameEvent(id, newName); - - replicate(new RenameEvent(id, newName)); + final Event toRename = eventsById.get(id); + if (toRename == null) { + throw new IllegalArgumentException("No sailing event with ID " + id + " found."); } + toRename.setName(newName); + mongoObjectFactory.renameEvent(id, newName); + replicate(new RenameEvent(id, newName)); } @Override @@ -2041,9 +2049,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport } protected void removeEventFromEventsById(Serializable id) { - synchronized (eventsById) { - eventsById.remove(id); - } + eventsById.remove(id); } @Override @@ -2070,25 +2076,21 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public CourseArea addCourseArea(UUID eventId, String courseAreaName, UUID courseAreaId) { CourseArea courseArea = getBaseDomainFactory().getOrCreateCourseArea(courseAreaId, courseAreaName); - synchronized (eventsById) { - addCourseAreaWithoutReplication(eventId, courseAreaId, courseAreaName); - replicate(new AddCourseArea(eventId, courseAreaName, courseAreaId)); - } + addCourseAreaWithoutReplication(eventId, courseAreaId, courseAreaName); + replicate(new AddCourseArea(eventId, courseAreaName, courseAreaId)); return courseArea; } @Override public CourseArea addCourseAreaWithoutReplication(UUID eventId, UUID courseAreaId, String courseAreaName) { final CourseArea courseArea = getBaseDomainFactory().getOrCreateCourseArea(courseAreaId, courseAreaName); - synchronized (eventsById) { - if (!eventsById.containsKey(eventId)) { - throw new IllegalArgumentException("No sailing event with ID " + eventId + " found."); - } - Event event = eventsById.get(eventId); - event.getVenue().addCourseArea(courseArea); - mongoObjectFactory.storeEvent(event); - return courseArea; + final Event event = eventsById.get(eventId); + if (event == null) { + throw new IllegalArgumentException("No sailing event with ID " + eventId + " found."); } + event.getVenue().addCourseArea(courseArea); + mongoObjectFactory.storeEvent(event); + return courseArea; } @Override @@ -2230,18 +2232,14 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public void createOrUpdateDeviceConfiguration(DeviceConfigurationMatcher matcher, DeviceConfiguration configuration) { - synchronized (configurationMap) { - configurationMap.put(matcher, configuration); - } + configurationMap.put(matcher, configuration); mongoObjectFactory.storeDeviceConfiguration(matcher, configuration); replicate(new CreateOrUpdateDeviceConfiguration(matcher, configuration)); } @Override public void removeDeviceConfiguration(DeviceConfigurationMatcher matcher) { - synchronized (configurationMap) { - configurationMap.remove(matcher); - } + configurationMap.remove(matcher); mongoObjectFactory.removeDeviceConfiguration(matcher); replicate(new RemoveDeviceConfiguration(matcher)); } From cf491754173e4bcc98c4e4fe98164a63d2a0db7c Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 14 May 2014 21:20:34 +0200 Subject: [PATCH 47/66] fixed the consistent use of raceTrackersByIDLocks for write access to raceTrackersByID --- .../server/impl/RacingEventServiceImpl.java | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 855d4285471..5d9dad086ea 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -212,9 +212,11 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport * not ideal due to its coarse-grained locking style which allows for little concurrency. Instead, this map is used * to keep locks for any ID that any invocation of the * {@link #addRace(RegattaIdentifier, RaceTrackingConnectivityParameters, long)} method is currently working on. - * Fetching or creating and putting a lock to this map happens in a synchronized method ({@link #getOrCreateRaceTrackersByIdLock}). - * When done, the {@link #addRace(RegattaIdentifier, RaceTrackingConnectivityParameters, long)} method cleans up by - * removing the lock again from this map, again using a synchronized method ({@link #removeRaceTrackersByIdLock}). + * Fetching or creating and putting a lock to this map happens in ({@link #getOrCreateRaceTrackersByIdLock}) which + * takes care of managing concurrent access to this concurrent map. When done, the + * {@link #addRace(RegattaIdentifier, RaceTrackingConnectivityParameters, long)} method cleans up by removing the + * lock again from this map, again using a synchronized method ( + * {@link #unlockRaceTrackersById(Object, NamedReentrantReadWriteLock)}). */ private final ConcurrentHashMap raceTrackersByIDLocks; @@ -1281,7 +1283,14 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport } } raceTracker.stop(); - raceTrackersByID.remove(raceTracker.getID()); + final Object trackerId = raceTracker.getID(); + final NamedReentrantReadWriteLock lock = lockRaceTrackersById(trackerId); + try { + raceTrackersByID.remove(trackerId); + } finally { + unlockRaceTrackersById(trackerId, lock); + } + raceTrackersByID.remove(trackerId); } raceTrackersByRegatta.remove(regatta); } @@ -1323,7 +1332,6 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport ScheduledFuture task = getScheduler().schedule(new Runnable() { @Override public void run() { - if (tracker.getRaces() == null || tracker.getRaces().isEmpty()) { try { Regatta regatta = tracker.getRegatta(); @@ -1337,7 +1345,13 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport trackersForRegatta.remove(tracker); } tracker.stop(); - raceTrackersByID.remove(tracker.getID()); + final Object trackerId = tracker.getID(); + final NamedReentrantReadWriteLock lock = lockRaceTrackersById(trackerId); + try { + raceTrackersByID.remove(trackerId); + } finally { + unlockRaceTrackersById(trackerId, lock); + } } if (trackersForRegatta == null || trackersForRegatta.isEmpty()) { stopTracking(regatta); @@ -1366,7 +1380,13 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport logger.info("Found tracker to stop for races " + raceTracker.getRaces()); raceTracker.stop(); trackerIter.remove(); - raceTrackersByID.remove(raceTracker.getID()); + final Object trackerId = raceTracker.getID(); + final NamedReentrantReadWriteLock lock = lockRaceTrackersById(trackerId); + try { + raceTrackersByID.remove(trackerId); + } finally { + unlockRaceTrackersById(trackerId, lock); + } } } } else { @@ -1515,7 +1535,13 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport raceTracker.stop(); // remove it from the raceTrackers by Regatta trackerIter.remove(); - raceTrackersByID.remove(raceTracker.getID()); + final Object trackerId = raceTracker.getID(); + final NamedReentrantReadWriteLock lock = lockRaceTrackersById(trackerId); + try { + raceTrackersByID.remove(trackerId); + } finally { + unlockRaceTrackersById(trackerId, lock); + } // if the last tracked race was removed, remove the entire regatta if (raceTrackersByRegatta.get(regatta).isEmpty()) { stopTracking(regatta); From de395104b185b6bedb12c7d0a43214cc34181e51 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 14 May 2014 21:23:40 +0200 Subject: [PATCH 48/66] turned regattasObservedForDefaultLeaderboard into a ConcurrentSkipListSet to avoid the use of synchronized --- .../server/impl/RacingEventServiceImpl.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 5d9dad086ea..0c8592ab32a 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -20,6 +20,7 @@ import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -242,7 +243,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport private final CompetitorStore competitorStore; - private Set regattasObservedForDefaultLeaderboard = new HashSet(); + private ConcurrentSkipListSet regattasObservedForDefaultLeaderboard = new ConcurrentSkipListSet(); private final MongoObjectFactory mongoObjectFactory; @@ -1110,20 +1111,14 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport /* raceDefinitionSetToUpdate */null); } - private void ensureRegattaIsObservedForDefaultLeaderboardAndAutoLeaderboardLinking( - DynamicTrackedRegatta trackedRegatta) { - synchronized (regattasObservedForDefaultLeaderboard) { - if (!regattasObservedForDefaultLeaderboard.contains(trackedRegatta)) { - trackedRegatta.addRaceListener(new RaceAdditionListener()); - regattasObservedForDefaultLeaderboard.add(trackedRegatta); - } + private void ensureRegattaIsObservedForDefaultLeaderboardAndAutoLeaderboardLinking(DynamicTrackedRegatta trackedRegatta) { + if (regattasObservedForDefaultLeaderboard.add(trackedRegatta)) { + trackedRegatta.addRaceListener(new RaceAdditionListener()); } } private void stopObservingRegattaForRedaultLeaderboardAndAutoLeaderboardLinking(DynamicTrackedRegatta trackedRegatta) { - synchronized (regattasObservedForDefaultLeaderboard) { - regattasObservedForDefaultLeaderboard.remove(trackedRegatta); - } + regattasObservedForDefaultLeaderboard.remove(trackedRegatta); } /** From 5a3defcf82eb07cd1213c2e02814210b550e41c1 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 14 May 2014 21:31:46 +0200 Subject: [PATCH 49/66] avoid needless iteration across all regattas' trackers when regatta is known in all callers --- .../test/DummyTrackedRegattaRegistry.java | 2 +- .../domain/test/LeaderboardOfflineTest.java | 2 +- .../sailing/domain/base/RaceDefinition.java | 1 - .../domain/base/impl/DomainFactoryImpl.java | 3 ++- .../tracking/TrackedRegattaRegistry.java | 2 +- .../gwt/ui/server/SailingServiceImpl.java | 2 +- .../server/test/RaceTrackerStartStopTest.java | 18 +++++++++--------- .../test/RacingEventServiceImplMock.java | 2 +- .../server/impl/RacingEventServiceImpl.java | 11 +++++------ 9 files changed, 21 insertions(+), 22 deletions(-) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DummyTrackedRegattaRegistry.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DummyTrackedRegattaRegistry.java index 1923389c476..14fcf36ecf1 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DummyTrackedRegattaRegistry.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DummyTrackedRegattaRegistry.java @@ -48,7 +48,7 @@ public class DummyTrackedRegattaRegistry implements TrackedRegattaRegistry { } @Override - public boolean isRaceBeingTracked(RaceDefinition r) { + public boolean isRaceBeingTracked(Regatta regattaContext, RaceDefinition r) { return false; } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java index dc4dfba57e4..354af34fed2 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java @@ -140,7 +140,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest { return null; } @Override - public boolean isRaceBeingTracked(RaceDefinition r) { + public boolean isRaceBeingTracked(Regatta regattaContext, RaceDefinition r) { return true; } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceDefinition.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceDefinition.java index 9699a1a7533..57e53313df5 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceDefinition.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceDefinition.java @@ -21,5 +21,4 @@ public interface RaceDefinition extends Named, WithID { Course getCourse(); Iterable getCompetitors(); - } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java index 6c2d58948d6..bfd8502c09f 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java @@ -112,7 +112,8 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl implements Domain // Optional: Getting the places of the race PlacemarkOrderDTO racePlaces = withGeoLocationData ? getRacePlaces(trackedRace) : null; TrackedRaceDTO trackedRaceDTO = createTrackedRaceDTO(trackedRace); - RaceDTO raceDTO = new RaceDTO(raceIdentifier, trackedRaceDTO, trackedRegattaRegistry.isRaceBeingTracked(trackedRace.getRace())); + RaceDTO raceDTO = new RaceDTO(raceIdentifier, trackedRaceDTO, trackedRegattaRegistry.isRaceBeingTracked( + trackedRace.getTrackedRegatta().getRegatta(), trackedRace.getRace())); raceDTO.places = racePlaces; updateRaceDTOWithTrackedRaceData(trackedRace, raceDTO); return raceDTO; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegattaRegistry.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegattaRegistry.java index 66ea8c44b34..b9f0bd9553a 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegattaRegistry.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegattaRegistry.java @@ -44,7 +44,7 @@ public interface TrackedRegattaRegistry { */ Regatta getRememberedRegattaForRace(Serializable raceID); - boolean isRaceBeingTracked(RaceDefinition r); + boolean isRaceBeingTracked(Regatta regattaContext, RaceDefinition r); /** * Stops all {@link RaceTracker}s currently tracking race. Note that if the same tracker also may have diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index ac00244f393..69b8ea08a60 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -900,7 +900,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S trackedRaceDTO = getBaseDomainFactory().createTrackedRaceDTO(trackedRace); } RaceWithCompetitorsDTO raceDTO = new RaceWithCompetitorsDTO(raceIdentifier, convertToCompetitorDTOs(r.getCompetitors()), - trackedRaceDTO, getService().isRaceBeingTracked(r)); + trackedRaceDTO, getService().isRaceBeingTracked(regatta, r)); if (trackedRace != null) { getBaseDomainFactory().updateRaceDTOWithTrackedRaceData(trackedRace, raceDTO); } diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java index 7a3a34aff88..4ee197a53ac 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java @@ -100,7 +100,7 @@ public class RaceTrackerStartStopTest { raceTrackerSet.add(raceTracker1); raceTrackerSet.add(raceTracker2); raceTrackerSet.add(raceTracker3); - racingEventService.getRaceTrackersByEventMap().put(regatta, raceTrackerSet); + racingEventService.getRaceTrackersByRegattaMap().put(regatta, raceTrackerSet); racingEventService.getRaceTrackersByIDMap().put(trackerID1, raceTracker1); racingEventService.getRaceTrackersByIDMap().put(trackerID2, raceTracker2); racingEventService.getRaceTrackersByIDMap().put(trackerID3, raceTracker3); @@ -136,8 +136,8 @@ public class RaceTrackerStartStopTest { assertFalse(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker3)); // The RaceTrakcersByEvent map should contain a tracker with a set of RaceDefinitions, containing the // raceDefinition1 - assertEquals(1, racingEventService.getRaceTrackersByEventMap().size()); - Iterator raceTrackerIter = racingEventService.getRaceTrackersByEventMap().get(regatta).iterator(); + assertEquals(1, racingEventService.getRaceTrackersByRegattaMap().size()); + Iterator raceTrackerIter = racingEventService.getRaceTrackersByRegattaMap().get(regatta).iterator(); while (raceTrackerIter.hasNext()) { RaceTracker currentTracker = raceTrackerIter.next(); assertSame(raceTracker1, currentTracker); @@ -165,9 +165,9 @@ public class RaceTrackerStartStopTest { } assertFalse(foundTrackedRaceForRaceDef2); // The trackers map should still contain the raceTrackers - assertTrue(racingEventService.getRaceTrackersByEventMap().get(regatta).contains(raceTracker1)); - assertTrue(racingEventService.getRaceTrackersByEventMap().get(regatta).contains(raceTracker2)); - assertTrue(racingEventService.getRaceTrackersByEventMap().get(regatta).contains(raceTracker3)); + assertTrue(racingEventService.getRaceTrackersByRegattaMap().get(regatta).contains(raceTracker1)); + assertTrue(racingEventService.getRaceTrackersByRegattaMap().get(regatta).contains(raceTracker2)); + assertTrue(racingEventService.getRaceTrackersByRegattaMap().get(regatta).contains(raceTracker3)); // The raceTrackerMap should still contain the raceTrackers. These raceTracker should not contain the raceDefinition raceDef2 anymore assertTrue(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker1)); assertTrue(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker2)); @@ -193,9 +193,9 @@ public class RaceTrackerStartStopTest { racingEventService.removeRace(regatta, raceDef1); racingEventService.removeRace(regatta, raceDef2); // The event map should still contain the raceTrackers except of raceTracker1 and raceTracker2 - assertFalse(racingEventService.getRaceTrackersByEventMap().get(regatta).contains(raceTracker1)); - assertFalse(racingEventService.getRaceTrackersByEventMap().get(regatta).contains(raceTracker2)); - assertTrue(racingEventService.getRaceTrackersByEventMap().get(regatta).contains(raceTracker3)); + assertFalse(racingEventService.getRaceTrackersByRegattaMap().get(regatta).contains(raceTracker1)); + assertFalse(racingEventService.getRaceTrackersByRegattaMap().get(regatta).contains(raceTracker2)); + assertTrue(racingEventService.getRaceTrackersByRegattaMap().get(regatta).contains(raceTracker3)); // The RaceTrackerByID map should still contain raceTracker3, but not raceTracker1 and raceTracker2 anymore assertFalse(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker1)); assertFalse(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker2)); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java index 8a02d1ae108..3c3771de8f3 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java @@ -26,7 +26,7 @@ public class RacingEventServiceImplMock extends RacingEventServiceImpl { return regattasByName; } - public Map> getRaceTrackersByEventMap() { + public Map> getRaceTrackersByRegattaMap() { return raceTrackersByRegatta; } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 0c8592ab32a..ef34486bc1d 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -838,13 +838,12 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport } @Override - public boolean isRaceBeingTracked(RaceDefinition r) { + public boolean isRaceBeingTracked(Regatta regattaContext, RaceDefinition r) { synchronized (raceTrackersByRegatta) { - for (Set trackers : raceTrackersByRegatta.values()) { - for (RaceTracker tracker : trackers) { - if (tracker.getRaces() != null && tracker.getRaces().contains(r)) { - return true; - } + Set trackers = raceTrackersByRegatta.get(regattaContext); + for (RaceTracker tracker : trackers) { + if (tracker.getRaces() != null && tracker.getRaces().contains(r)) { + return true; } } return false; From 587824d8075d4919d4b1acc3a00f22548733070c Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 14 May 2014 22:57:58 +0200 Subject: [PATCH 50/66] replaced use of synchronized for raceTrackersByRegatta by the use of a corresponding lock --- .../test/RacingEventServiceImplMock.java | 4 +- .../server/impl/RacingEventServiceImpl.java | 203 ++++++++++-------- 2 files changed, 113 insertions(+), 94 deletions(-) diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java index 3c3771de8f3..2635ced08f6 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java @@ -1,7 +1,7 @@ package com.sap.sailing.server.test; import java.util.Map; -import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.impl.DataImportProgressImpl; @@ -26,7 +26,7 @@ public class RacingEventServiceImplMock extends RacingEventServiceImpl { return regattasByName; } - public Map> getRaceTrackersByRegattaMap() { + public Map> getRaceTrackersByRegattaMap() { return raceTrackersByRegatta; } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index ef34486bc1d..a202ed4d3f0 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -197,7 +197,13 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport private final ConcurrentHashMap courseListeners; - protected final ConcurrentHashMap> raceTrackersByRegatta; + protected final ConcurrentHashMap> raceTrackersByRegatta; + + /** + * Although {@link #raceTrackersByRegatta} is a concurrent hash map, entering sets as values needs to be + * synchronized using this lock's write lock to avoid two value sets overwriting each other. + */ + private final NamedReentrantReadWriteLock raceTrackersByRegattaLock; /** * Remembers the trackers by paramURL/liveURI/storedURI to avoid duplication @@ -378,6 +384,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport eventsById = new ConcurrentHashMap(); regattaTrackingCache = new ConcurrentHashMap<>(); raceTrackersByRegatta = new ConcurrentHashMap<>(); + raceTrackersByRegattaLock = new NamedReentrantReadWriteLock("raceTrackersByRegatta for "+this, /* fair */ false); raceTrackersByID = new ConcurrentHashMap<>(); raceTrackersByIDLocks = new ConcurrentHashMap<>(); leaderboardGroupsByName = new ConcurrentHashMap<>(); @@ -839,15 +846,16 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public boolean isRaceBeingTracked(Regatta regattaContext, RaceDefinition r) { - synchronized (raceTrackersByRegatta) { - Set trackers = raceTrackersByRegatta.get(regattaContext); + Set trackers = raceTrackersByRegatta.get(regattaContext); + if (trackers != null) { for (RaceTracker tracker : trackers) { - if (tracker.getRaces() != null && tracker.getRaces().contains(r)) { + final Set races = tracker.getRaces(); + if (races != null && races.contains(r)) { return true; } } - return false; } + return false; } @Override @@ -991,14 +999,17 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport tracker = params.createRaceTracker(regatta, this, windStore, gpsFixStore); assert tracker.getRegatta() == regatta; } - synchronized (raceTrackersByRegatta) { + LockUtil.lockForWrite(raceTrackersByRegattaLock); + try { raceTrackersByID.put(trackerID, tracker); - Set trackers = raceTrackersByRegatta.get(tracker.getRegatta()); + ConcurrentSkipListSet trackers = raceTrackersByRegatta.get(tracker.getRegatta()); if (trackers == null) { - trackers = new HashSet(); + trackers = new ConcurrentSkipListSet(); raceTrackersByRegatta.put(tracker.getRegatta(), trackers); } trackers.add(tracker); + } finally { + LockUtil.unlockAfterWrite(raceTrackersByRegattaLock); } // TODO we assume here that the event name is unique which necessitates adding the boat class name to it in RegattaImpl constructor String regattaName = tracker.getRegatta().getName(); @@ -1267,26 +1278,30 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public void stopTracking(Regatta regatta) throws MalformedURLException, IOException, InterruptedException { - synchronized (raceTrackersByRegatta) { - if (raceTrackersByRegatta.containsKey(regatta)) { - for (RaceTracker raceTracker : raceTrackersByRegatta.get(regatta)) { - final Set races = raceTracker.getRaces(); - if (races != null) { - for (RaceDefinition race : races) { - stopTrackingWind(regatta, race); - } + final ConcurrentSkipListSet trackersForRegatta = raceTrackersByRegatta.get(regatta); + if (trackersForRegatta != null) { + for (RaceTracker raceTracker : trackersForRegatta) { + final Set races = raceTracker.getRaces(); + if (races != null) { + for (RaceDefinition race : races) { + stopTrackingWind(regatta, race); } - raceTracker.stop(); - final Object trackerId = raceTracker.getID(); - final NamedReentrantReadWriteLock lock = lockRaceTrackersById(trackerId); - try { - raceTrackersByID.remove(trackerId); - } finally { - unlockRaceTrackersById(trackerId, lock); - } - raceTrackersByID.remove(trackerId); } + raceTracker.stop(); + final Object trackerId = raceTracker.getID(); + final NamedReentrantReadWriteLock lock = lockRaceTrackersById(trackerId); + try { + raceTrackersByID.remove(trackerId); + } finally { + unlockRaceTrackersById(trackerId, lock); + } + raceTrackersByID.remove(trackerId); + } + LockUtil.lockForWrite(raceTrackersByRegattaLock); + try { raceTrackersByRegatta.remove(regatta); + } finally { + LockUtil.unlockAfterWrite(raceTrackersByRegattaLock); } } } @@ -1332,20 +1347,17 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport logger.log(Level.SEVERE, "RaceDefinition for a race in regatta " + regatta.getName() + " not obtained within " + timeoutInMilliseconds + "ms. Aborting tracker for this race."); - Set trackersForRegatta; - synchronized (raceTrackersByRegatta) { - trackersForRegatta = raceTrackersByRegatta.get(regatta); - if (trackersForRegatta != null) { - trackersForRegatta.remove(tracker); - } - tracker.stop(); - final Object trackerId = tracker.getID(); - final NamedReentrantReadWriteLock lock = lockRaceTrackersById(trackerId); - try { - raceTrackersByID.remove(trackerId); - } finally { - unlockRaceTrackersById(trackerId, lock); - } + Set trackersForRegatta = raceTrackersByRegatta.get(regatta); + if (trackersForRegatta != null) { + trackersForRegatta.remove(tracker); + } + tracker.stop(); + final Object trackerId = tracker.getID(); + final NamedReentrantReadWriteLock lock = lockRaceTrackersById(trackerId); + try { + raceTrackersByID.remove(trackerId); + } finally { + unlockRaceTrackersById(trackerId, lock); } if (trackersForRegatta == null || trackersForRegatta.isEmpty()) { stopTracking(regatta); @@ -1364,33 +1376,31 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport public void stopTracking(Regatta regatta, RaceDefinition race) throws MalformedURLException, IOException, InterruptedException { logger.info("Stopping tracking for " + race + "..."); - synchronized (raceTrackersByRegatta) { - final Set trackerSet = raceTrackersByRegatta.get(regatta); - if (raceTrackersByRegatta.containsKey(regatta)) { - Iterator trackerIter = trackerSet.iterator(); - while (trackerIter.hasNext()) { - RaceTracker raceTracker = trackerIter.next(); - if (raceTracker.getRaces() != null && raceTracker.getRaces().contains(race)) { - logger.info("Found tracker to stop for races " + raceTracker.getRaces()); - raceTracker.stop(); - trackerIter.remove(); - final Object trackerId = raceTracker.getID(); - final NamedReentrantReadWriteLock lock = lockRaceTrackersById(trackerId); - try { - raceTrackersByID.remove(trackerId); - } finally { - unlockRaceTrackersById(trackerId, lock); - } + final Set trackerSet = raceTrackersByRegatta.get(regatta); + if (trackerSet != null) { + Iterator trackerIter = trackerSet.iterator(); + while (trackerIter.hasNext()) { + RaceTracker raceTracker = trackerIter.next(); + if (raceTracker.getRaces() != null && raceTracker.getRaces().contains(race)) { + logger.info("Found tracker to stop for races " + raceTracker.getRaces()); + raceTracker.stop(); + trackerIter.remove(); + final Object trackerId = raceTracker.getID(); + final NamedReentrantReadWriteLock lock = lockRaceTrackersById(trackerId); + try { + raceTrackersByID.remove(trackerId); + } finally { + unlockRaceTrackersById(trackerId, lock); } } - } else { - logger.warning("Didn't find any trackers for regatta " + regatta); - } - stopTrackingWind(regatta, race); - // if the last tracked race was removed, confirm that tracking for the entire regatta has stopped - if (trackerSet == null || trackerSet.isEmpty()) { - stopTracking(regatta); } + } else { + logger.warning("Didn't find any trackers for regatta " + regatta); + } + stopTrackingWind(regatta, race); + // if the last tracked race was removed, confirm that tracking for the entire regatta has stopped + if (trackerSet == null || trackerSet.isEmpty()) { + stopTracking(regatta); } } @@ -1511,35 +1521,33 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport */ private void stopAllTrackersForWhichRaceIsLastReachable(Regatta regatta, RaceDefinition race) throws MalformedURLException, IOException, InterruptedException { - synchronized (raceTrackersByRegatta) { - if (raceTrackersByRegatta.containsKey(regatta)) { - Iterator trackerIter = raceTrackersByRegatta.get(regatta).iterator(); - while (trackerIter.hasNext()) { - RaceTracker raceTracker = trackerIter.next(); - if (raceTracker.getRaces() != null && raceTracker.getRaces().contains(race)) { - boolean foundReachableRace = false; - for (RaceDefinition raceTrackedByTracker : raceTracker.getRaces()) { - if (raceTrackedByTracker != race && isReachable(regatta, raceTrackedByTracker)) { - foundReachableRace = true; - break; - } + if (raceTrackersByRegatta.containsKey(regatta)) { + Iterator trackerIter = raceTrackersByRegatta.get(regatta).iterator(); + while (trackerIter.hasNext()) { + RaceTracker raceTracker = trackerIter.next(); + if (raceTracker.getRaces() != null && raceTracker.getRaces().contains(race)) { + boolean foundReachableRace = false; + for (RaceDefinition raceTrackedByTracker : raceTracker.getRaces()) { + if (raceTrackedByTracker != race && isReachable(regatta, raceTrackedByTracker)) { + foundReachableRace = true; + break; } - if (!foundReachableRace) { - // firstly stop the tracker - raceTracker.stop(); - // remove it from the raceTrackers by Regatta - trackerIter.remove(); - final Object trackerId = raceTracker.getID(); - final NamedReentrantReadWriteLock lock = lockRaceTrackersById(trackerId); - try { - raceTrackersByID.remove(trackerId); - } finally { - unlockRaceTrackersById(trackerId, lock); - } - // if the last tracked race was removed, remove the entire regatta - if (raceTrackersByRegatta.get(regatta).isEmpty()) { - stopTracking(regatta); - } + } + if (!foundReachableRace) { + // firstly stop the tracker + raceTracker.stop(); + // remove it from the raceTrackers by Regatta + trackerIter.remove(); + final Object trackerId = raceTracker.getID(); + final NamedReentrantReadWriteLock lock = lockRaceTrackersById(trackerId); + try { + raceTrackersByID.remove(trackerId); + } finally { + unlockRaceTrackersById(trackerId, lock); + } + // if the last tracked race was removed, remove the entire regatta + if (raceTrackersByRegatta.get(regatta).isEmpty()) { + stopTracking(regatta); } } } @@ -1925,7 +1933,18 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport logger.info("Clearing all data structures..."); regattaTrackingCache.clear(); - leaderboardGroupsByName.clear(); + LockUtil.lockForWrite(raceTrackersByRegattaLock); + try { + raceTrackersByRegatta.clear(); + } finally { + LockUtil.unlockAfterWrite(raceTrackersByRegattaLock); + } + LockUtil.lockForWrite(leaderboardGroupsByNameLock); + try { + leaderboardGroupsByName.clear(); + } finally { + LockUtil.unlockAfterWrite(leaderboardGroupsByNameLock); + } LockUtil.lockForWrite(leaderboardsByNameLock); try { leaderboardsByName.clear(); From 77bea92fdee633472d21f69973a0033e8b72d0a8 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 14 May 2014 23:13:16 +0200 Subject: [PATCH 51/66] implemented locking instead of synchronization (see bug 1935) for regattaTrackingCache --- .../server/impl/RacingEventServiceImpl.java | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index a202ed4d3f0..4b2d3419e44 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -256,6 +256,12 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport private final DomainObjectFactory domainObjectFactory; private final ConcurrentHashMap regattaTrackingCache; + + /** + * Protects write access transactions that do a previous read to {@link #regattaTrackingCache}; read-only + * access is already synchronized by using a concurrent hash map for {@link #regattaTrackingCache}. + */ + private final NamedReentrantReadWriteLock regattaTrackingCacheLock; private final ConcurrentHashMap operationExecutionListeners; @@ -383,6 +389,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport regattasByName = new ConcurrentHashMap(); eventsById = new ConcurrentHashMap(); regattaTrackingCache = new ConcurrentHashMap<>(); + regattaTrackingCacheLock = new NamedReentrantReadWriteLock("regattaTrackingCache for "+this, /* fair */ false); raceTrackersByRegatta = new ConcurrentHashMap<>(); raceTrackersByRegattaLock = new NamedReentrantReadWriteLock("raceTrackersByRegatta for "+this, /* fair */ false); raceTrackersByID = new ConcurrentHashMap<>(); @@ -1313,7 +1320,12 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport if (regatta.getName() != null) { logger.info("Removing regatta " + regatta.getName() + " (" + regatta.hashCode() + ") from " + this); regattasByName.remove(regatta.getName()); - regattaTrackingCache.remove(regatta); + LockUtil.lockForWrite(regattaTrackingCacheLock); + try { + regattaTrackingCache.remove(regatta); + } finally { + LockUtil.unlockAfterWrite(regattaTrackingCacheLock); + } regatta.removeRegattaListener(this); regatta.removeRaceColumnListener(raceLogReplicator); regatta.removeRaceColumnListener(raceLogScoringReplicator); @@ -1604,7 +1616,8 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public DynamicTrackedRegatta getOrCreateTrackedRegatta(Regatta regatta) { cacheAndReplicateDefaultRegatta(regatta); - synchronized (regattaTrackingCache) { + LockUtil.lockForWrite(regattaTrackingCacheLock); + try { DynamicTrackedRegatta result = regattaTrackingCache.get(regatta); if (result == null) { logger.info("Creating DynamicTrackedRegattaImpl for regatta " + regatta.getName() + " with hashCode " @@ -1615,6 +1628,8 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport ensureRegattaIsObservedForDefaultLeaderboardAndAutoLeaderboardLinking(result); } return result; + } finally { + LockUtil.unlockAfterWrite(regattaTrackingCacheLock); } } @@ -1626,7 +1641,13 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public void removeTrackedRegatta(Regatta regatta) { logger.info("Removing regatta " + regatta.getName() + " from regattaTrackingCache"); - DynamicTrackedRegatta trackedRegatta = regattaTrackingCache.remove(regatta); + final DynamicTrackedRegatta trackedRegatta; + LockUtil.lockForWrite(regattaTrackingCacheLock); + try { + trackedRegatta = regattaTrackingCache.remove(regatta); + } finally { + LockUtil.unlockAfterWrite(regattaTrackingCacheLock); + } stopObservingRegattaForRedaultLeaderboardAndAutoLeaderboardLinking(trackedRegatta); } @@ -1932,7 +1953,12 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport } logger.info("Clearing all data structures..."); - regattaTrackingCache.clear(); + LockUtil.lockForWrite(regattaTrackingCacheLock); + try { + regattaTrackingCache.clear(); + } finally { + LockUtil.unlockAfterRead(regattaTrackingCacheLock); + } LockUtil.lockForWrite(raceTrackersByRegattaLock); try { raceTrackersByRegatta.clear(); From cdac14475927ff0930d3eb7d3966ca656fc42f7e Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 14 May 2014 23:19:42 +0200 Subject: [PATCH 52/66] fixed type of tracker set to ConcurrentSkipListSet in test mock after changing it in RacingEventServiceImpl --- .../sailing/server/test/RaceTrackerStartStopTest.java | 9 +++++---- .../sailing/server/test/RacingEventServiceImplMock.java | 6 +----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java index 4ee197a53ac..93c7a7948f5 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java @@ -15,6 +15,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ConcurrentSkipListSet; import org.junit.Before; import org.junit.Test; @@ -50,7 +51,7 @@ public class RaceTrackerStartStopTest { private RacingEventServiceImplMock racingEventService; private Regatta regatta; private BoatClass boatClass; - private Set raceTrackerSet = new HashSet(); + private ConcurrentSkipListSet raceTrackerSet = new ConcurrentSkipListSet(); private RaceDefinition raceDef1; private RaceDefinition raceDef2; @@ -66,10 +67,10 @@ public class RaceTrackerStartStopTest { boatClass = new BoatClassImpl(BOATCLASSNAME, /* typicallyStartsUpwind */ true); regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, EVENTNAME, boatClass, /* trackedRegattaRegistry */ null, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), UUID.randomUUID(), null); - racingEventService.getEventsByName().put(EVENTNAME, regatta); + racingEventService.getRegattasByName().put(EVENTNAME, regatta); TrackedRegatta trackedRegatta1 = racingEventService.getOrCreateTrackedRegatta(regatta); - racingEventService.getEventsByNameMap().put(EVENTNAME, regatta); - raceTrackerSet = new HashSet(); + racingEventService.getRegattasByName().put(EVENTNAME, regatta); + raceTrackerSet = new ConcurrentSkipListSet(); raceDef1 = new RaceDefinitionImpl(RACENAME1, new CourseImpl("Course1", new ArrayList()), boatClass, new ArrayList()); raceDef2 = new RaceDefinitionImpl(RACENAME2, new CourseImpl("Course2", new ArrayList()), boatClass, new ArrayList()); raceDef3 = new RaceDefinitionImpl(RACENAME3, new CourseImpl("Course3", new ArrayList()), boatClass, new ArrayList()); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java index 2635ced08f6..3f46d665763 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java @@ -22,10 +22,6 @@ public class RacingEventServiceImplMock extends RacingEventServiceImpl { lock.addProgress(dataImportProgressImpl.getOperationId(), dataImportProgressImpl); } - public Map getEventsByNameMap() { - return regattasByName; - } - public Map> getRaceTrackersByRegattaMap() { return raceTrackersByRegatta; } @@ -34,7 +30,7 @@ public class RacingEventServiceImplMock extends RacingEventServiceImpl { return raceTrackersByID; } - public Map getEventsByName() { + public Map getRegattasByName() { return regattasByName; } From 9c9f8b4693834976d799e96ab82b89fbc664f2dd Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 14 May 2014 23:42:32 +0200 Subject: [PATCH 53/66] simplified construction of MeasurementXMLFile for Jenkins/Hudson measurements; use this to measure locking performance --- .../sap/sailing/domain/test/LockTraceTest.java | 15 +++++++++++++-- .../MarkPassingCalculatorPerformanceTest.java | 4 +--- .../test/measurements/MeasurementXMLFile.java | 4 ++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LockTraceTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LockTraceTest.java index 814131f7e03..82c31cfdee6 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LockTraceTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LockTraceTest.java @@ -4,12 +4,16 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import org.junit.Ignore; import org.junit.Test; +import com.sap.sailing.domain.test.measurements.Measurement; +import com.sap.sailing.domain.test.measurements.MeasurementCase; +import com.sap.sailing.domain.test.measurements.MeasurementXMLFile; import com.sap.sailing.util.impl.LockUtil; import com.sap.sailing.util.impl.NamedReentrantReadWriteLock; @@ -54,15 +58,22 @@ public class LockTraceTest { } @Test - public void testLockingPerformance() { + public void testLockingPerformance() throws IOException { NamedReentrantReadWriteLock lock = new NamedReentrantReadWriteLock("Lock", /* fair */ true); long start = System.currentTimeMillis(); - for (int i=0; i<100000; i++) { + final int count = 100000; + for (int i=0; i result = new LinkedHashMap<>(); - private static String className; public MarkPassingCalculatorPerformanceTest() { super(); - className = getClass().getName(); } @AfterClass public static void createXMLFile() throws IOException { - MeasurementXMLFile performanceReport = new MeasurementXMLFile("TEST-MarkPassingCaculatorPerformanceTest.xml", "MarkPassingCaculatorPerformanceTest", className); + MeasurementXMLFile performanceReport = new MeasurementXMLFile(MarkPassingCalculatorPerformanceTest.class); for (String key : result.keySet()) { MeasurementCase performanceReportCase = performanceReport.addCase(key); performanceReportCase.addMeasurement(new Measurement(key, result.get(key))); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/measurements/MeasurementXMLFile.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/measurements/MeasurementXMLFile.java index 072598f56af..05bbe657eb1 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/measurements/MeasurementXMLFile.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/measurements/MeasurementXMLFile.java @@ -27,6 +27,10 @@ public class MeasurementXMLFile { private final Set cases; private final String testClassName; + public MeasurementXMLFile(Class testClass) { + this("TEST-"+testClass.getSimpleName()+".xml", testClass.getSimpleName(), testClass.getName()); + } + public MeasurementXMLFile(String reportFileName, String testSuiteName, String testClassName) { this.reportFileName = reportFileName; this.testSuiteName = testSuiteName; From 17e58bb6cca8c1fdcbb7bee2a5383cc4e0fc693d Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 14 May 2014 21:46:30 +0000 Subject: [PATCH 54/66] Added hint on how to use MeasurementXMLFile to plot measurements in Hudson/Jenkins --- wiki/ci.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/wiki/ci.md b/wiki/ci.md index 0b3d1aaef3f..b7c46b972ac 100644 --- a/wiki/ci.md +++ b/wiki/ci.md @@ -4,6 +4,16 @@ Our default Hudson runs on http://hudson.sapsailing.com. If you need access, ple It is good practice to set up a new Hudson job for major branches that require solid testing before being merged into the master branch. The entry page at http://hudson.sapsailing.com explains how to do this. It basically comes down to copying a template job and adjusting the branch name. As easy as that :-) +## Collecting measurements using Hudson/Jenkins + +If you have a test case that measures something, such as performance or level of fulfillment or any other numeric measure, you can have Hudson/Jenkins plot it. In your test case, use the class `com.sap.sailing.domain.test.measurements.MeasurementXMLFile` and add performance cases to which you add measurements, e.g., as follows: +
+        MeasurementXMLFile performanceReport = new MeasurementXMLFile(getClass());
+        MeasurementCase performanceReportCase = performanceReport.addCase(getClass().getSimpleName());
+        performanceReportCase.addMeasurement(new Measurement("My Measurement", theNumberIMeasured));
+        performanceReport.write();
+
+ ## In case you'd like to set up your own Hudson/Jenkins Initially we had trouble with Jenkins and the GIT plug-in. However, https://issues.jenkins-ci.org/browse/JENKINS-13381?focusedCommentId=196689&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-196689 explains that installing the Credentials plugin into Jenkins may help. Basically, what is needed over and above a plain Jenkins installation are the following plug-ins: From 56b61bfc21b4e9bcebf455b326f4c649d62ebaba Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 14 May 2014 23:50:39 +0200 Subject: [PATCH 55/66] added test for re-entrance of write locks with in-between read lock --- .../sailing/domain/test/LockTraceTest.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) mode change 100755 => 100644 java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LockTraceTest.java diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LockTraceTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LockTraceTest.java old mode 100755 new mode 100644 index 82c31cfdee6..6fb5335226f --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LockTraceTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LockTraceTest.java @@ -56,6 +56,28 @@ public class LockTraceTest { assertFalse(lock.getReaders().contains(Thread.currentThread())); assertEquals(0, lock.getReadHoldCount()); } + + @Test + public void testReentrantWriteLockingWithInBetweenReadLocking() { + NamedReentrantReadWriteLock lock = new NamedReentrantReadWriteLock("testReentrantWriteLockingWithInBetweenReadLocking-Lock", /* fair */ false); + LockUtil.lockForWrite(lock); + assertTrue(lock.getWriter() == Thread.currentThread()); + LockUtil.lockForRead(lock); // reentrant read while holding write + assertTrue(lock.getReaders().contains(Thread.currentThread())); + LockUtil.lockForWrite(lock); // reentrant write + assertTrue(lock.getWriter() == Thread.currentThread()); + assertTrue(lock.getReaders().contains(Thread.currentThread())); + LockUtil.unlockAfterRead(lock); + assertFalse(lock.getReaders().contains(Thread.currentThread())); + assertEquals(0, lock.getReadHoldCount()); + assertTrue(lock.getWriter() == Thread.currentThread()); + assertFalse(lock.getReaders().contains(Thread.currentThread())); + assertEquals(0, lock.getReadHoldCount()); + LockUtil.unlockAfterWrite(lock); + assertTrue(lock.getWriter() == Thread.currentThread()); + LockUtil.unlockAfterWrite(lock); + assertFalse(lock.getWriter() == Thread.currentThread()); + } @Test public void testLockingPerformance() throws IOException { From aff7ecef7aa3df1876a23b292f7e42c151d0667b Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 15 May 2014 00:04:54 +0200 Subject: [PATCH 56/66] added missing concurrency protection to regattasByName by using a lock --- .../sailing/domain/test/LockTraceTest.java | 0 .../sap/sailing/server/impl/MediaLibrary.java | 3 + .../server/impl/RacingEventServiceImpl.java | 124 +++++++++++++----- 3 files changed, 91 insertions(+), 36 deletions(-) mode change 100644 => 100755 java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LockTraceTest.java diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LockTraceTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LockTraceTest.java old mode 100644 new mode 100755 diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/MediaLibrary.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/MediaLibrary.java index 95954af54c8..d244702e18a 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/MediaLibrary.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/MediaLibrary.java @@ -269,6 +269,9 @@ class MediaLibrary { } } + /** + * Returns a non-live copy of the media tracks + */ Collection allTracks() { readLock.lock(); try { diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 4b2d3419e44..b20c581af83 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -194,6 +194,8 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport * {@link Regatta} objects that exist outside this service for regattas not (yet) registered here. */ protected final ConcurrentHashMap regattasByName; + + private final NamedReentrantReadWriteLock regattasByNameLock; private final ConcurrentHashMap courseListeners; @@ -387,6 +389,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport this.dataImportLock = new DataImportLockWithProgress(); regattasByName = new ConcurrentHashMap(); + regattasByNameLock = new NamedReentrantReadWriteLock("regattasByName for "+this, /* fair */ false); eventsById = new ConcurrentHashMap(); regattaTrackingCache = new ConcurrentHashMap<>(); regattaTrackingCacheLock = new NamedReentrantReadWriteLock("regattaTrackingCache for "+this, /* fair */ false); @@ -430,20 +433,20 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public void clearState() throws Exception { - for (String leaderboardGroupName : this.leaderboardGroupsByName.keySet()) { + for (String leaderboardGroupName : new ArrayList<>(this.leaderboardGroupsByName.keySet())) { removeLeaderboardGroup(leaderboardGroupName); } - for (String leaderboardName : this.leaderboardsByName.keySet()) { + for (String leaderboardName : new ArrayList<>(this.leaderboardsByName.keySet())) { removeLeaderboard(leaderboardName); } - for(Regatta regatta : this.regattasByName.values()) { + for (Regatta regatta : new ArrayList<>(this.regattasByName.values())) { stopTracking(regatta); removeRegatta(regatta); } - for(Event event : this.eventsById.values()) { + for (Event event : new ArrayList<>(this.eventsById.values())) { removeEvent(event.getId()); } - for(MediaTrack mediaTrack : this.mediaLibrary.allTracks()) { + for (MediaTrack mediaTrack : this.mediaLibrary.allTracks()) { mediaTrackDeleted(mediaTrack); } this.competitorStore.clear(); @@ -473,12 +476,17 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport } private void loadStoredRegattas() { - for (Regatta regatta : domainObjectFactory.loadAllRegattas(this)) { - logger.info("putting regatta " + regatta.getName() + " (" + regatta.hashCode() + ") into regattasByName"); - regattasByName.put(regatta.getName(), regatta); - regatta.addRegattaListener(this); - regatta.addRaceColumnListener(raceLogReplicator); - regatta.addRaceColumnListener(raceLogScoringReplicator); + LockUtil.lockForWrite(regattasByNameLock); + try { + for (Regatta regatta : domainObjectFactory.loadAllRegattas(this)) { + logger.info("putting regatta " + regatta.getName() + " (" + regatta.hashCode() + ") into regattasByName"); + regattasByName.put(regatta.getName(), regatta); + regatta.addRegattaListener(this); + regatta.addRaceColumnListener(raceLogReplicator); + regatta.addRaceColumnListener(raceLogScoringReplicator); + } + } finally { + LockUtil.unlockAfterWrite(regattasByNameLock); } } @@ -913,14 +921,23 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport Regatta regatta = new RegattaImpl(raceLogStore, baseRegattaName, getBaseDomainFactory().getOrCreateBoatClass( boatClassName), series, persistent, scoringScheme, id, courseArea); boolean wasCreated = false; + // try a quick read protected by the concurrent hash map implementation if (!regattasByName.containsKey(regatta.getName())) { - wasCreated = true; - logger.info("putting regatta " + regatta.getName() + " (" + regatta.hashCode() - + ") into regattasByName of " + this); - regattasByName.put(regatta.getName(), regatta); - regatta.addRegattaListener(this); - regatta.addRaceColumnListener(raceLogReplicator); - regatta.addRaceColumnListener(raceLogScoringReplicator); + LockUtil.lockForWrite(regattasByNameLock); + try { + // check again, now that we hold the exclusive write lock + if (!regattasByName.containsKey(regatta.getName())) { + wasCreated = true; + logger.info("putting regatta " + regatta.getName() + " (" + regatta.hashCode() + + ") into regattasByName of " + this); + regattasByName.put(regatta.getName(), regatta); + regatta.addRegattaListener(this); + regatta.addRaceColumnListener(raceLogReplicator); + regatta.addRaceColumnListener(raceLogScoringReplicator); + } + } finally { + LockUtil.unlockAfterWrite(regattasByNameLock); + } } logger.info("Created regatta " + regatta.getName() + " (" + hashCode() + ") on " + this); if (persistent) { @@ -1026,8 +1043,13 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport if (regattaWithName != tracker.getRegatta()) { if (Util.isEmpty(regattaWithName.getAllRaces())) { // probably, tracker removed the last races from the old regatta and created a new one - regattasByName.remove(regattaName); - cacheAndReplicateDefaultRegatta(tracker.getRegatta()); + LockUtil.lockForWrite(regattasByNameLock); + try { + regattasByName.remove(regattaName); + cacheAndReplicateDefaultRegatta(tracker.getRegatta()); + } finally { + LockUtil.unlockAfterWrite(regattasByNameLock); + } } else { throw new RuntimeException("Internal error. Two regatta objects with equal name " + regattaName); } @@ -1101,19 +1123,29 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport * contained {@link Regatta#getAllRaces() races} are replicated to all replica. */ private void cacheAndReplicateDefaultRegatta(Regatta regatta) { + // try a quick read first, protected by regattasByName being a concurrent hash set if (!regattasByName.containsKey(regatta.getName())) { - logger.info("putting regatta " + regatta.getName() + " (" + regatta.hashCode() - + ") into regattasByName of " + this); - regattasByName.put(regatta.getName(), regatta); - regatta.addRegattaListener(this); - regatta.addRaceColumnListener(raceLogReplicator); - regatta.addRaceColumnListener(raceLogScoringReplicator); + // now we need to obtain exclusive write access; in between, some other thread may have added a regatta by that + // name, so we need to check again: + LockUtil.lockForWrite(regattasByNameLock); + try { + if (!regattasByName.containsKey(regatta.getName())) { + logger.info("putting regatta " + regatta.getName() + " (" + regatta.hashCode() + + ") into regattasByName of " + this); + regattasByName.put(regatta.getName(), regatta); + regatta.addRegattaListener(this); + regatta.addRaceColumnListener(raceLogReplicator); + regatta.addRaceColumnListener(raceLogScoringReplicator); - replicate(new AddDefaultRegatta(regatta.getBaseName(), regatta.getBoatClass() == null ? null : regatta - .getBoatClass().getName(), regatta.getId())); - RegattaIdentifier regattaIdentifier = regatta.getRegattaIdentifier(); - for (RaceDefinition race : regatta.getAllRaces()) { - replicate(new AddRaceDefinition(regattaIdentifier, race)); + replicate(new AddDefaultRegatta(regatta.getBaseName(), regatta.getBoatClass() == null ? null + : regatta.getBoatClass().getName(), regatta.getId())); + RegattaIdentifier regattaIdentifier = regatta.getRegattaIdentifier(); + for (RaceDefinition race : regatta.getAllRaces()) { + replicate(new AddRaceDefinition(regattaIdentifier, race)); + } + } + } finally { + LockUtil.unlockAfterWrite(regattasByNameLock); } } } @@ -1319,7 +1351,12 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport if (regatta != null) { if (regatta.getName() != null) { logger.info("Removing regatta " + regatta.getName() + " (" + regatta.hashCode() + ") from " + this); - regattasByName.remove(regatta.getName()); + LockUtil.lockForWrite(regattasByNameLock); + try { + regattasByName.remove(regatta.getName()); + } finally { + LockUtil.unlockAfterWrite(regattasByNameLock); + } LockUtil.lockForWrite(regattaTrackingCacheLock); try { regattaTrackingCache.remove(regatta); @@ -1438,7 +1475,12 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport if (regatta.isPersistent()) { mongoObjectFactory.removeRegatta(regatta); } - regattasByName.remove(regatta.getName()); + LockUtil.lockForWrite(regattasByNameLock); + try { + regattasByName.remove(regatta.getName()); + } finally { + LockUtil.unlockAfterWrite(regattasByNameLock); + } regatta.removeRegattaListener(this); regatta.removeRaceColumnListener(raceLogReplicator); regatta.removeRaceColumnListener(raceLogScoringReplicator); @@ -1517,11 +1559,16 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport } } } - // remove the race from the regatta if the regatta is not persistently stored + // remove the race from the (default) regatta if the regatta is not persistently stored regatta.removeRace(race); if (!regatta.isPersistent() && Util.isEmpty(regatta.getAllRaces())) { logger.info("Removing regatta " + regatta.getName() + " (" + regatta.hashCode() + ") from service " + this); - regattasByName.remove(regatta.getName()); + LockUtil.lockForWrite(regattasByNameLock); + try { + regattasByName.remove(regatta.getName()); + } finally { + LockUtil.unlockAfterWrite(regattasByNameLock); + } regatta.removeRegattaListener(this); regatta.removeRaceColumnListener(raceLogReplicator); regatta.removeRaceColumnListener(raceLogScoringReplicator); @@ -1941,7 +1988,12 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport // de-serialization; this will cause all classes to be visible that this bundle // (com.sap.sailing.server) can see Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); - regattasByName.clear(); + LockUtil.lockForWrite(regattasByNameLock); + try { + regattasByName.clear(); + } finally { + LockUtil.unlockAfterWrite(regattasByNameLock); + } regattasObservedForDefaultLeaderboard.clear(); if (raceTrackersByRegatta != null && !raceTrackersByRegatta.isEmpty()) { From 102caa8f767349bbc846ed4e3b585dd36553841c Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 15 May 2014 07:37:21 +0200 Subject: [PATCH 57/66] replaced ConcurrentSkipListSet by Collections.newSetFromMap(new ConcurrentHashMap()) which doesn't require comparability --- java/com.sap.sailing.gwt.ui/SailingGWT.launch | 118 +++++++++--------- .../server/test/RaceTrackerStartStopTest.java | 6 +- .../test/RacingEventServiceImplMock.java | 4 +- .../server/impl/RacingEventServiceImpl.java | 14 ++- 4 files changed, 72 insertions(+), 70 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT.launch b/java/com.sap.sailing.gwt.ui/SailingGWT.launch index 6cf37e1ef58..5d943eeb607 100755 --- a/java/com.sap.sailing.gwt.ui/SailingGWT.launch +++ b/java/com.sap.sailing.gwt.ui/SailingGWT.launch @@ -1,59 +1,59 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java index 93c7a7948f5..5c23fafb198 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java @@ -15,7 +15,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.UUID; -import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.ConcurrentHashMap; import org.junit.Before; import org.junit.Test; @@ -51,7 +51,7 @@ public class RaceTrackerStartStopTest { private RacingEventServiceImplMock racingEventService; private Regatta regatta; private BoatClass boatClass; - private ConcurrentSkipListSet raceTrackerSet = new ConcurrentSkipListSet(); + private Set raceTrackerSet = Collections.newSetFromMap(new ConcurrentHashMap()); private RaceDefinition raceDef1; private RaceDefinition raceDef2; @@ -70,7 +70,7 @@ public class RaceTrackerStartStopTest { racingEventService.getRegattasByName().put(EVENTNAME, regatta); TrackedRegatta trackedRegatta1 = racingEventService.getOrCreateTrackedRegatta(regatta); racingEventService.getRegattasByName().put(EVENTNAME, regatta); - raceTrackerSet = new ConcurrentSkipListSet(); + raceTrackerSet = Collections.newSetFromMap(new ConcurrentHashMap()); raceDef1 = new RaceDefinitionImpl(RACENAME1, new CourseImpl("Course1", new ArrayList()), boatClass, new ArrayList()); raceDef2 = new RaceDefinitionImpl(RACENAME2, new CourseImpl("Course2", new ArrayList()), boatClass, new ArrayList()); raceDef3 = new RaceDefinitionImpl(RACENAME3, new CourseImpl("Course3", new ArrayList()), boatClass, new ArrayList()); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java index 3f46d665763..b486b20d692 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RacingEventServiceImplMock.java @@ -1,7 +1,7 @@ package com.sap.sailing.server.test; import java.util.Map; -import java.util.concurrent.ConcurrentSkipListSet; +import java.util.Set; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.impl.DataImportProgressImpl; @@ -22,7 +22,7 @@ public class RacingEventServiceImplMock extends RacingEventServiceImpl { lock.addProgress(dataImportProgressImpl.getOperationId(), dataImportProgressImpl); } - public Map> getRaceTrackersByRegattaMap() { + public Map> getRaceTrackersByRegattaMap() { return raceTrackersByRegatta; } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index b20c581af83..c918fe77082 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -20,7 +20,6 @@ import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -199,7 +198,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport private final ConcurrentHashMap courseListeners; - protected final ConcurrentHashMap> raceTrackersByRegatta; + protected final ConcurrentHashMap> raceTrackersByRegatta; /** * Although {@link #raceTrackersByRegatta} is a concurrent hash map, entering sets as values needs to be @@ -251,7 +250,10 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport private final CompetitorStore competitorStore; - private ConcurrentSkipListSet regattasObservedForDefaultLeaderboard = new ConcurrentSkipListSet(); + /** + * A set based on a concurrent hash map, therefore being thread safe + */ + private Set regattasObservedForDefaultLeaderboard = Collections.newSetFromMap(new ConcurrentHashMap()); private final MongoObjectFactory mongoObjectFactory; @@ -1026,9 +1028,9 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport LockUtil.lockForWrite(raceTrackersByRegattaLock); try { raceTrackersByID.put(trackerID, tracker); - ConcurrentSkipListSet trackers = raceTrackersByRegatta.get(tracker.getRegatta()); + Set trackers = raceTrackersByRegatta.get(tracker.getRegatta()); if (trackers == null) { - trackers = new ConcurrentSkipListSet(); + trackers = Collections.newSetFromMap(new ConcurrentHashMap()); raceTrackersByRegatta.put(tracker.getRegatta(), trackers); } trackers.add(tracker); @@ -1317,7 +1319,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public void stopTracking(Regatta regatta) throws MalformedURLException, IOException, InterruptedException { - final ConcurrentSkipListSet trackersForRegatta = raceTrackersByRegatta.get(regatta); + final Set trackersForRegatta = raceTrackersByRegatta.get(regatta); if (trackersForRegatta != null) { for (RaceTracker raceTracker : trackersForRegatta) { final Set races = raceTracker.getRaces(); From 6d9cfbd6378939cfeca9f9a8fdb56b7d8c5ce2f2 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 15 May 2014 09:44:40 +0200 Subject: [PATCH 58/66] fixed an unlocking statement (had accidentally used unlockAfterRead after a lockForWrite) --- .../src/com/sap/sailing/server/impl/RacingEventServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index c918fe77082..e5b5033333d 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -2011,7 +2011,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport try { regattaTrackingCache.clear(); } finally { - LockUtil.unlockAfterRead(regattaTrackingCacheLock); + LockUtil.unlockAfterWrite(regattaTrackingCacheLock); } LockUtil.lockForWrite(raceTrackersByRegattaLock); try { From 98707dfc9bc79614bb431a81190200cf2f626440 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 15 May 2014 10:52:26 +0200 Subject: [PATCH 59/66] determine boat class from race ID in SwissTiming replay adapter; fixes a bug introduced by a refactoring that tries to get the boat class from Manage2Sail --- .../impl/SwissTimingReplayToDomainAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java index 3dd2b14e647..a81b3ac2a40 100755 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java @@ -233,7 +233,7 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter { short ctPoints_x10_Winner) { if (boatType == BoatType.Competitor) { Competitor competitor = domainFactory.createCompetitorWithoutID(sailNumberOrTrackerID, threeLetterIOCCode.trim(), name.trim(), - currentRaceID, null /* boat class */); + currentRaceID, domainFactory.getRaceTypeFromRaceID(currentRaceID).getBoatClass()); Set competitorsOfCurrentRace = competitorsPerRaceID.get(currentRaceID); if (competitorsOfCurrentRace == null) { competitorsOfCurrentRace = new HashSet<>(); @@ -306,7 +306,7 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter { private void createRace() { final Regatta myRegatta = regatta != null ? regatta : domainFactory.getOrCreateDefaultRegatta(EmptyRaceLogStore.INSTANCE, - currentRaceID, null /* boat class */, trackedRegattaRegistry); + currentRaceID, domainFactory.getRaceTypeFromRaceID(currentRaceID).getBoatClass(), trackedRegattaRegistry); RaceDefinition race = domainFactory.createRaceDefinition(myRegatta, currentRaceID, competitorsPerRaceID.get(currentRaceID), currentCourseDefinition); racePerRaceID.put(currentRaceID, race); From 0abe9c4a41b09f2840c3979ff3a61ed8bf945515 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 15 May 2014 10:58:46 +0200 Subject: [PATCH 60/66] added a test case that without the fix of 98707dfc9bc79614bb431a81190200cf2f626440 fails --- .../test/SwissTimingReplayAdapterServiceTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java b/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java index cb6c4a969a5..ac12e4994be 100644 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java @@ -3,6 +3,7 @@ package com.sap.sailing.domain.swisstimingreplayadapter.test; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; @@ -195,6 +196,7 @@ public class SwissTimingReplayAdapterServiceTest { Iterable trackedRaces = replayListener.getTrackedRaces(); TrackedRace trackedRace = trackedRaces.iterator().next(); for (Competitor competitor : trackedRace.getRace().getCompetitors()) { + assertSame(DomainFactory.INSTANCE.getBaseDomainFactory().getOrCreateBoatClass("470"), competitor.getBoat().getBoatClass()); Distance distanceToLineAtStart = trackedRace.getDistanceToStartLine(competitor, trackedRace.getStartOfRace()); assertTrue(distanceToLineAtStart.getMeters() > 0); assertTrue(distanceToLineAtStart.getMeters() < 8.5); From 5349f5459ecdd84c4932549e5e9497f0282693f9 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 15 May 2014 11:34:21 +0200 Subject: [PATCH 61/66] fixed NPE in TeamImpl.getNationality() in case there was no nationality set yet --- .../src/com/sap/sailing/domain/base/impl/TeamImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TeamImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TeamImpl.java index f2bb400b44e..ae7c8db34fe 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TeamImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TeamImpl.java @@ -89,7 +89,7 @@ public class TeamImpl extends NamedImpl implements DynamicTeam { @Override public Nationality getNationality() { - return getNationalityDonor().getNationality(); + return getNationalityDonor() == null ? null : getNationalityDonor().getNationality(); } private WithNationality getNationalityDonor() { From 4e3769e527493685d9cfee24e018b7c11047a205 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 15 May 2014 11:44:27 +0200 Subject: [PATCH 62/66] added test and fix for nationality setting and null handling on Competitor / Team --- .../com/sap/sailing/domain/base/impl/TeamImpl.java | 3 +++ .../sailing/domain/test/CompetitorListenerTest.java | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TeamImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TeamImpl.java index ae7c8db34fe..2f456d302f0 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TeamImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TeamImpl.java @@ -111,6 +111,9 @@ public class TeamImpl extends NamedImpl implements DynamicTeam { for (Person sailor : getSailors()) { ((DynamicPerson) sailor).setNationality(newNationality); } + if (getCoach() != null) { + getCoach().setNationality(newNationality); + } for (NationalityChangeListener listener : getNationalityChangeListeners()) { listener.nationalityChanged(this, oldNationality, newNationality); } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorListenerTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorListenerTest.java index 08741bc72b1..a719b7c4496 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorListenerTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorListenerTest.java @@ -2,6 +2,7 @@ package com.sap.sailing.domain.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; @@ -97,6 +98,18 @@ public class CompetitorListenerTest extends AbstractSerializationTest { competitor.addCompetitorChangeListener(listener); } + @Test + public void testNullNationality() { + competitor.getTeam().setNationality(null); + assertNull(competitor.getTeam().getNationality()); + competitor.getTeam().setNationality(null); + assertNull(competitor.getTeam().getNationality()); + competitor.getTeam().setNationality(baseDomainFactory.getOrCreateNationality("GER")); + assertEquals(baseDomainFactory.getOrCreateNationality("GER"), competitor.getTeam().getNationality()); + competitor.getTeam().setNationality(null); + assertNull(competitor.getTeam().getNationality()); + } + @Test public void testSimpleCompetitorListenerPatternForName() { competitor.setName("Dr. Hasso Plattner"); From 0ba17dae43a4857ba23f6207d27d87b9057e3332 Mon Sep 17 00:00:00 2001 From: Frank Mittag Date: Thu, 15 May 2014 12:46:45 +0000 Subject: [PATCH 63/66] Updated amazon ec2 (markdown) --- wiki/amazon-ec2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wiki/amazon-ec2.md b/wiki/amazon-ec2.md index ea1d441454e..04bbbaef528 100644 --- a/wiki/amazon-ec2.md +++ b/wiki/amazon-ec2.md @@ -60,7 +60,7 @@ USE_ENVIRONMENT= #### Receiving wind from Expedition -- To receive and forward wind with an Expedition connector, log into webserver as user trac and switch to $HOME/udpmirror. Start the mirror and forward it to the instance you want. In order to receive wind through the Igtimi connector, this step is not required as the wind data is received directly from the Igtimi server. +- To receive and forward wind with an Expedition connector, log into webserver as user trac and switch to $HOME/servers/udpmirror. Start the mirror and forward it to the instance you want. In order to receive wind through the Igtimi connector, this step is not required as the wind data is received directly from the Igtimi server. #### Setting up Master and Replica From 2018364a557a719fe4cab03f0ae600db71fbf8c3 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 15 May 2014 15:24:53 +0200 Subject: [PATCH 64/66] keep de-serialization compatible with earlier versions by creating the lock object if it doesn't exist in readObject --- .../sap/sailing/domain/base/impl/VenueImpl.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/VenueImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/VenueImpl.java index 8fc9d75308d..d33ee69e283 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/VenueImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/VenueImpl.java @@ -1,5 +1,7 @@ package com.sap.sailing.domain.base.impl; +import java.io.IOException; +import java.io.ObjectInputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -19,12 +21,23 @@ public class VenueImpl implements Venue { */ private final List courseAreas; - private final NamedReentrantReadWriteLock courseAreasLock; + private NamedReentrantReadWriteLock courseAreasLock; public VenueImpl(String name) { this.name = name; courseAreas = new ArrayList(); - courseAreasLock = new NamedReentrantReadWriteLock("Course Areas for venue "+name, /* fair */ false); + courseAreasLock = createCourseAreasLock(name); + } + + private NamedReentrantReadWriteLock createCourseAreasLock(String name) { + return new NamedReentrantReadWriteLock("Course Areas for venue "+name, /* fair */ false); + } + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + ois.defaultReadObject(); + if (courseAreasLock == null) { + courseAreasLock = createCourseAreasLock(getName()); + } } @Override From 52ea7b1e57e4b262a174dffc82639638aba07c78 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 15 May 2014 16:25:25 +0200 Subject: [PATCH 65/66] working on bug 1954: first of all, reducing the number of constructor invocatoins for NaturalComparator --- .../domain/common/impl/NaturalComparator.java | 4 +-- .../common/test/NaturalComparatorTest.java | 11 +++++++ .../adminconsole/CompetitorTableWrapper.java | 6 ++-- .../gwt/ui/adminconsole/TableWrapper.java | 3 -- java/target/http2udpmirror | 1 - java/target/http2udpmirror_axel | 31 +++++++++---------- 6 files changed, 32 insertions(+), 24 deletions(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/NaturalComparator.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/NaturalComparator.java index 33a2d247546..f27c8d433c0 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/NaturalComparator.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/NaturalComparator.java @@ -13,7 +13,7 @@ import java.util.Comparator; public class NaturalComparator implements Comparator { boolean caseSensitive; - public NaturalComparator(boolean caseSensitive){ + public NaturalComparator(boolean caseSensitive) { this.caseSensitive = caseSensitive; } @@ -26,7 +26,7 @@ public class NaturalComparator implements Comparator { */ @Override public int compare(String a, String b) { - if(!caseSensitive){ + if (!caseSensitive){ a = a.toLowerCase(); b = a.toLowerCase(); } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/NaturalComparatorTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/NaturalComparatorTest.java index a3dd0421262..8012198284a 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/NaturalComparatorTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/NaturalComparatorTest.java @@ -3,6 +3,10 @@ package com.sap.sailing.domain.common.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + import org.junit.Before; import org.junit.Test; @@ -103,5 +107,12 @@ public class NaturalComparatorTest { assertTrue(comparator.compare("0.01", "0.2") < 0); assertTrue(comparator.compare("0.1.5b", "0.2a") < 0); } + + @Test + public void testSortSailIDs() { + List list = Arrays.asList("Groupama", "GER8829", "A", "FRA 56893", "Cam", "GAC Pindar", "Grün", "a/m", "d/n"); + Collections.sort(list, new NaturalComparator(/* case sensitive */ false)); + assertTrue(list.indexOf("A") < list.indexOf("Cam")); + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorTableWrapper.java index 38fee020d78..389018f79b7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorTableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorTableWrapper.java @@ -58,9 +58,10 @@ public class CompetitorTableWrapper extends TableWrapper() { + private final NaturalComparator comparator = new NaturalComparator(/* caseSensitive */ false); @Override public int compare(CompetitorDTO o1, CompetitorDTO o2) { - return new NaturalComparator(false).compare(o1.getBoatClass().getName(), o2.getBoatClass().getName()); + return comparator.compare(o1.getBoatClass().getName(), o2.getBoatClass().getName()); } }); @@ -86,9 +87,10 @@ public class CompetitorTableWrapper extends TableWrapper() { + private final NaturalComparator comparator = new NaturalComparator(/* case sensitive */ false); @Override public int compare(CompetitorDTO o1, CompetitorDTO o2) { - return new NaturalComparator(false).compare(o1.getSailID(), o2.getSailID()); + return comparator.compare(o1.getSailID(), o2.getSailID()); } }); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TableWrapper.java index b6a633b09d9..893021f914a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TableWrapper.java @@ -39,12 +39,9 @@ public abstract class TableWrapper> implements Is this.selectionModel = selectionModel; this.dataProvider = new ListDataProvider(); mainPanel = new VerticalPanel(); - table = new CellTable(10000, tableRes); - dataProvider.addDataDisplay(table); table.setSelectionModel(selectionModel); - mainPanel.add(table); } diff --git a/java/target/http2udpmirror b/java/target/http2udpmirror index 2d2a00486ba..de1e1ea58bb 100755 --- a/java/target/http2udpmirror +++ b/java/target/http2udpmirror @@ -1,5 +1,4 @@ #!/bin/sh -#!/bin/sh target="`dirname \"$0\"`" pwd=`pwd` echo "$target" | grep "^/" diff --git a/java/target/http2udpmirror_axel b/java/target/http2udpmirror_axel index cf7f7e0c5dd..b15ffc64a73 100755 --- a/java/target/http2udpmirror_axel +++ b/java/target/http2udpmirror_axel @@ -1,16 +1,15 @@ -#!/bin/sh -#!/bin/sh -target="`dirname \"$0\"`" -pwd=`pwd` -echo "$target" | grep "^/" -if [ "$0" != "0" ]; then - # no leading slash, therefore no absolute path; prepend pwd - target="$pwd/$target" -fi -target=c:/data/SAP/sailing/workspace/java/target -echo target is $target -cd "$target/../com.sap.sailing.feature.p2build/bin/products/raceanalysis.product.id/win32/win32/x86_64" -echo pwd is now `pwd` -classpath="`find plugins -name 'com.sap.sailing.expeditionconnector_*.jar' | sort | tail -1`;`find plugins -name 'com.sap.sailing.domain.common_*.jar' | sort | tail -1`" -echo Using classpath $classpath -"$JAVA_HOME/bin/java" -cp "$classpath" -Djava.util.logging.config.file=$target/configuration/logging.properties com.sap.sailing.expeditionconnector.FromHttpToUdpMirror $* +#!/bin/sh +target="`dirname \"$0\"`" +pwd=`pwd` +echo "$target" | grep "^/" +if [ "$0" != "0" ]; then + # no leading slash, therefore no absolute path; prepend pwd + target="$pwd/$target" +fi +target=c:/data/SAP/sailing/workspace/java/target +echo target is $target +cd "$target/../com.sap.sailing.feature.p2build/bin/products/raceanalysis.product.id/win32/win32/x86_64" +echo pwd is now `pwd` +classpath="`find plugins -name 'com.sap.sailing.expeditionconnector_*.jar' | sort | tail -1`;`find plugins -name 'com.sap.sailing.domain.common_*.jar' | sort | tail -1`" +echo Using classpath $classpath +"$JAVA_HOME/bin/java" -cp "$classpath" -Djava.util.logging.config.file=$target/configuration/logging.properties com.sap.sailing.expeditionconnector.FromHttpToUdpMirror $* From 13f7f196f93f5d8b1421f7ee99155b6129509d16 Mon Sep 17 00:00:00 2001 From: Fredrik Teschke Date: Thu, 15 May 2014 18:29:19 +0000 Subject: [PATCH 66/66] Updated racelog tracking (markdown) --- wiki/racelog-tracking.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wiki/racelog-tracking.md b/wiki/racelog-tracking.md index 4fe725e8b74..ea7be159200 100644 --- a/wiki/racelog-tracking.md +++ b/wiki/racelog-tracking.md @@ -22,8 +22,8 @@ Following the numbers in the diagram, this is a possible race log tracking scena 1. The race is defined through events that are added to the RaceLog. 2. The RaceTracker is created. 3. As soon as the "StartTrackingEvent" is added to the RaceLog, the RaceTracker creates the TrackedRace. -4. Tracking devices submit GPS fixes to the tracking adapter. -5. If these fixes match the mappings defined for this race in the RaceLog, they are added to the TrackedRace. +4. Tracking devices submit GPS fixes to the tracking adapter. This adds these fixes to the global GPSFixStore. +5. The GPSFixStore notifies its listeners - among them the RaceTrackers - of new fixes. Each RaceTracker checks whether a new fix matches the mappings defined in its RaceLog, and if so adds it to the TrackedRace. Not all identifiers represent actual class or bundle names. For a more technical documentation, refer to the JavaDoc in the various ``*.racelog.tracking`` packages and the ``com.sap.sailing.domain.racelogtrackingadapter`` bundle. The following interfaces and classes are good starting points: * DeviceIdentifier