mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-09 13:50:57 +00:00
GWT streamlets in simulator;
This commit is contained in:
1 parent
15af984a94
commit
c83196f73d
10 files changed
+806
-70
No files matched your search
@@ -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;
|
||||
}
|
||||
}
|
||||
+20
@@ -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;
|
||||
}
|
||||
}
|
||||
+53
-23
@@ -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;
|
||||
}
|
||||
}
|
||||
+297
@@ -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<SimulatorWindDTO> 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<steps; s++) {
|
||||
for(int i=0; i<imax; i++) {
|
||||
for(int j=0; j<jmax; j++) {
|
||||
SimulatorWindDTO wind = gridData.get(p);
|
||||
p++;
|
||||
if (wind.trueWindSpeedInKnots > 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;
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+34
-15
@@ -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<particles.length; idx++) {
|
||||
Particle particle = particles[idx];
|
||||
if ((particle.age > 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+40
@@ -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();
|
||||
|
||||
}
|
||||
+34
-30
@@ -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
|
||||
|
||||
+304
@@ -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<SimulatorWindDTO> 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<steps; s++) {
|
||||
jsonData +="[";
|
||||
for(int i=0; i<imax; i++) {
|
||||
jsonData +="[";
|
||||
for(int j=0; j<jmax; j++) {
|
||||
SimulatorWindDTO wind = data.get(p);
|
||||
p++;
|
||||
if (wind.trueWindSpeedInKnots > 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;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user