mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-06 12:23:55 +00:00
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;
This commit is contained in:
1 parent
e4ff29f52b
commit
2e2820aed1
7 files changed
+342
-116
No files matched your search
+24
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+24
-19
@@ -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) {
|
||||
|
||||
+3
-5
@@ -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;
|
||||
|
||||
}
|
||||
+55
-54
@@ -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;
|
||||
};*/
|
||||
}
|
||||
|
||||
}
|
||||
+50
-6
@@ -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
|
||||
|
||||
+12
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+174
-32
@@ -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<particles.length; idx++) {
|
||||
LatLng q = field.getRandomPosition();
|
||||
public Particle[] makeNewParticles() {
|
||||
|
||||
Particle[] newParticles = new Particle[nParticles];
|
||||
|
||||
for(int idx=0; idx<newParticles.length; idx++) {
|
||||
GeoPos q = field.getRandomPosition();
|
||||
Particle particle = new Particle();
|
||||
particle.pos = q;
|
||||
particle.age = Math.max(2, (int)Math.round(Math.random()*40));
|
||||
particle.pxOld = projection.latlng2pixel(particle.pos);
|
||||
particle.pxOld = projection.latlng2pixel(q);
|
||||
particle.alpha = 0;
|
||||
particle.v = field.getValue(q);
|
||||
particles[idx] = particle;
|
||||
particle.v = field.getVector(q);
|
||||
newParticles[idx] = particle;
|
||||
}
|
||||
|
||||
return newParticles;
|
||||
}
|
||||
|
||||
public void onBoundsChanged() {
|
||||
projection.clearCanvas();
|
||||
swarmPause = 5;
|
||||
}
|
||||
|
||||
public void onMoveEnd() {
|
||||
|
||||
}
|
||||
|
||||
public void updateBounds() {
|
||||
|
||||
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));
|
||||
|
||||
Vector visibleNE = this.isVisible(fieldNE);
|
||||
Vector visibleSW = this.isVisible(fieldSW);
|
||||
|
||||
boolean useBoundsNorth = (visibleNE.y == 0);
|
||||
boolean useBoundsEast = (visibleNE.x == 0);
|
||||
boolean useBoundsSouth = (visibleSW.y == 0);
|
||||
boolean 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 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<particles.length; idx++) {
|
||||
Particle particle = particles[idx];
|
||||
if (particle.age == 0) {
|
||||
continue;
|
||||
}
|
||||
ctxt.setLineWidth(1.0);
|
||||
//nDrawn++;
|
||||
ctxt.setLineWidth(field.lineWidth(particle.alpha));
|
||||
ctxt.setStrokeStyle(color[particle.alpha]);
|
||||
ctxt.beginPath();
|
||||
ctxt.moveTo(particle.pxOld.getX(), particle.pxOld.getY());
|
||||
ctxt.moveTo(particle.pxOld.x, particle.pxOld.y);
|
||||
particle.pxOld = projection.latlng2pixel(particle.pos);
|
||||
ctxt.lineTo(particle.pxOld.getX(), particle.pxOld.getY());
|
||||
ctxt.lineTo(particle.pxOld.x, particle.pxOld.y);
|
||||
ctxt.stroke();
|
||||
}
|
||||
}
|
||||
//console("drawn particles:"+nDrawn);
|
||||
}
|
||||
|
||||
|
||||
/* public void swarmStart() {
|
||||
int millis = 40; //opt_millis; // || 20;
|
||||
//self = this;
|
||||
//function go() {
|
||||
Date start = new Date();
|
||||
if (!execute()) {
|
||||
return;
|
||||
}
|
||||
Date time = new Date();
|
||||
int passed = (int)(time.getTime() - start.getTime());
|
||||
setTimeout(swarmStart(), Math.max(10, passed));
|
||||
//}
|
||||
//go();
|
||||
}*/
|
||||
|
||||
public boolean execute() {
|
||||
|
||||
double off = 0.01;
|
||||
double speed = 0.01*field.motionScale(map.getZoom());
|
||||
for(int idx=0; idx<particles.length; idx++) {
|
||||
Particle particle = particles[idx];
|
||||
Point v = null;
|
||||
Vector v = null;
|
||||
if (particle.age <= 0) {
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
particle.pos = field.getRandomPosition();
|
||||
v = field.getValue(particle.pos);
|
||||
v = field.getVector(particle.pos);
|
||||
double weight = field.particleWeight(particle.pos, v);
|
||||
if ((weight >= 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user