Merge branch 'origin/simulator_maps_v3'

This commit is contained in:
Christopher Ronnewinkel (D036654) committed 2014-04-01 22:00:27 +02:00
commit da4757c90e
20 files changed
+120619 -461

No files matched your search

@@ -32,6 +32,7 @@ bin.includes = META-INF/,\
com.sap.sailing.gwt.ui.Simulator/,\
Simulator.html,\
Simulator.css,\
wind-streamlets.js,\
RegattaOverview.css,\
RegattaOverview.html,\
com.sap.sailing.gwt.ui.RegattaOverview/,\
@@ -217,7 +217,8 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
wf.setGridAreaGps(gridAreaGps);
}
WindFieldDTO wfDTO = createWindFieldDTO(wf, startTime, endTime, timeStep, params.isShowLines(), params.getSeedLines());
WindFieldDTO wfDTO = createWindFieldDTO(wf, startTime, endTime, timeStep, params); //params.isShowStreamlets2(), params.isShowLines(), params.getSeedLines());
LOGGER.info("Exiting getWindField");
return wfDTO;
@@ -299,7 +300,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
WindFieldDTO windFieldDTO = null;
if (pattern != null) {
windFieldDTO = this.createWindFieldDTO(wf, startTime, endTime, timeStep, params.isShowLines(), params.getSeedLines());
windFieldDTO = this.createWindFieldDTO(wf, startTime, endTime, timeStep, params); // params.isShowStreamlets2(), params.isShowLines(), params.getSeedLines());
}
return new SimulatorResultsDTO(rcDTO, pathDTOs, windFieldDTO, simulatedPaths.notificationMessage);
@@ -721,40 +722,112 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
return result;
}
private WindFieldDTO createWindFieldDTO(WindFieldGenerator wf, TimePoint startTime, TimePoint endTime, TimePoint timeStep, boolean isShowLines,
char seedLines) {
private WindFieldDTO createWindFieldDTO(WindFieldGenerator wf, TimePoint startTime, TimePoint endTime, TimePoint timeStep, WindFieldGenParamsDTO params) {
//boolean isStreamlets, boolean isShowLines, char seedLines) {
WindFieldDTO windFieldDTO = new WindFieldDTO();
List<SimulatorWindDTO> wList = new ArrayList<SimulatorWindDTO>();
Position[][] positionGrid = wf.getPositionGrid();
//if (!params.isShowStreamlets2()) {
List<SimulatorWindDTO> wList = new ArrayList<SimulatorWindDTO>();
if (positionGrid != null && positionGrid.length > 0) {
TimePoint t = startTime;
while (t.compareTo(endTime) <= 0) {
for (int i = 0; i < positionGrid.length; ++i) {
for (int j = 0; j < positionGrid[i].length; ++j) {
Wind localWind = wf.getWind(new TimedPositionWithSpeedImpl(t, positionGrid[i][j], null));
LOGGER.finer(localWind.toString());
wList.add(createSimulatorWindDTO(localWind));
}
}
t = new MillisecondsTimePoint(t.asMillis() + timeStep.asMillis());
}
TimePoint t = startTime;
while (t.compareTo(endTime) <= 0) {
for (int i = 0; i < positionGrid.length; ++i) {
for (int j = 0; j < positionGrid[i].length; ++j) {
Wind localWind = wf.getWind(new TimedPositionWithSpeedImpl(t, positionGrid[i][j], null));
LOGGER.finer(localWind.toString());
wList.add(createSimulatorWindDTO(localWind));
}
}
t = new MillisecondsTimePoint(t.asMillis() + timeStep.asMillis());
}
}
windFieldDTO.setMatrix(wList);
if (isShowLines && seedLines == 'f') {
this.getWindLinesFromStartLine(wf, windFieldDTO, startTime, endTime, timeStep);
if (params.isShowLines() && params.getSeedLines() == 'f') {
this.getWindLinesFromStartLine(wf, windFieldDTO, startTime, endTime, timeStep);
}
if (isShowLines && seedLines == 'b') {
this.getWindLinesFromEndLine(wf, windFieldDTO, startTime, endTime, timeStep);
if (params.isShowLines() && params.getSeedLines() == 'b') {
this.getWindLinesFromEndLine(wf, windFieldDTO, startTime, endTime, timeStep);
}
windFieldDTO.curBearing = wf.getWindParameters().curBearing;
windFieldDTO.curSpeed = wf.getWindParameters().curSpeed;
//} else {
if (params.isShowStreamlets2()) {
// generate JSON-vectorfield
String jsonField = "$wnd.windFieldSim={";
//this.rcStart = field.rcStart;
jsonField += "rcStart:{lat:" + params.getNorthWest().latDeg + ",lng:" + params.getNorthWest().lngDeg + "},";
//this.rcEnd = field.rcEnd;
jsonField += "rcEnd:{lat:" + params.getSouthEast().latDeg + ",lng:" + params.getSouthEast().lngDeg + "},";
// SouthWest
jsonField += "boundsSW:{lat:" + positionGrid[0][0].getLatDeg() + ",lng:" + positionGrid[0][0].getLngDeg() + "},";
// NorthEast
jsonField += "boundsNE:{lat:" + positionGrid[params.getyRes()-1][params.getxRes()-1].getLatDeg() + ",lng:" + positionGrid[params.getyRes()-1][params.getxRes()-1].getLngDeg() + "},";
//this.resY = field.resY;
jsonField += "resY:" + params.getyRes() + ",";
//this.resX = field.resX;
jsonField += "resX:" + params.getxRes() + ",";
//this.xScale = field.xScale;
jsonField += "xScale:1.5,";
/*String jsonField2 = "data:[";
DecimalFormat df = new DecimalFormat("#.00");
double maxWindSpeed = 0;
//List<SimulatorWindDTO> windMat = wfDTO.getMatrix();
if (positionGrid != null && positionGrid.length > 0) {
TimePoint t = startTime;
while (t.compareTo(endTime) <= 0) {
jsonField2 += "[";
for (int i = 0; i < positionGrid.length; ++i) {
jsonField2 += "[";
for (int j = 0; j < positionGrid[i].length; ++j) {
Wind localWind = wf.getWind(new TimedPositionWithSpeedImpl(t, positionGrid[i][j], null));
if (localWind.getKnots() > maxWindSpeed) {
maxWindSpeed = localWind.getKnots();
}
double windY = localWind.getKnots() * Math.cos(localWind.getBearing().getRadians());
double windX = localWind.getKnots() * Math.sin(localWind.getBearing().getRadians());
jsonField2 += df.format(windX) + "," + df.format(windY);
if (j<(params.getxRes()-1)) {
jsonField2 += ",";
}
}
if (i<(params.getyRes()-1)) {
jsonField2 += "],";
} else {
jsonField2 += "]";
}
}
t = new MillisecondsTimePoint(t.asMillis() + timeStep.asMillis());
if (t.compareTo(endTime) <= 0) {
jsonField2 += "],";
} else {
jsonField2 += "]";
}
}
}
jsonField += "maxLength:" + maxWindSpeed + "," + jsonField2;
jsonField += "]";
// finalize JSON Field
jsonField += "};";*/
//LOGGER.info("JSON Field:\n" + jsonField);
windFieldDTO.windDataJSON = jsonField;
// windFieldDTO.setMatrix(null);
}
return windFieldDTO;
}
@@ -9,6 +9,8 @@ public class WindFieldDTO implements IsSerializable {
public double curSpeed;
public double curBearing;
public String windDataJSON;
private List<SimulatorWindDTO> matrix;
private WindLinesDTO windLinesDTO;
@@ -35,6 +35,7 @@ public class WindFieldGenParamsDTO implements IsSerializable {
private boolean showGrid;
private boolean showStreamlets;
private boolean showStreamlets2;
private boolean showLines;
@@ -149,6 +150,14 @@ public class WindFieldGenParamsDTO implements IsSerializable {
this.showStreamlets = showStreamlets;
}
public boolean isShowStreamlets2() {
return showStreamlets2;
}
public void setShowStreamlets2(boolean showStreamlets2) {
this.showStreamlets2 = showStreamlets2;
}
public boolean isShowLines() {
return showLines;
}
@@ -34,6 +34,18 @@ public class GoogleMapStyleHelper {
return result;
}
public static MapTypeStyle createElementStyleOnlyLightness(MapTypeStyleFeatureType featureType, MapTypeStyleElementType elementType, int ligthness) {
MapTypeStyle result = MapTypeStyle.newInstance();
result.setFeatureType(featureType);
result.setElementType(elementType);
MapTypeStyler[] typeStylers = new MapTypeStyler[1];
typeStylers[0] = MapTypeStyler.newLightnessStyler(ligthness);
result.setStylers(typeStylers);
return result;
}
public static MapTypeStyle createElementStyleWithVisibility(MapTypeStyleFeatureType featureType, MapTypeStyleElementType elementType, String visibility) {
MapTypeStyle result = MapTypeStyle.newInstance();
@@ -9,10 +9,14 @@ import com.google.gwt.maps.client.base.Point;
import com.google.gwt.maps.client.events.click.ClickMapEvent;
import com.google.gwt.maps.client.events.click.ClickMapHandler;
import com.google.gwt.maps.client.mvc.MVCArray;
import com.google.gwt.maps.client.overlays.MapCanvasProjection;
import com.google.gwt.maps.client.overlays.Marker;
import com.google.gwt.maps.client.overlays.MarkerOptions;
import com.google.gwt.maps.client.overlays.OverlayView;
import com.google.gwt.maps.client.overlays.Polyline;
import com.google.gwt.maps.client.overlays.PolylineOptions;
import com.google.gwt.maps.client.overlays.overlayhandlers.OverlayViewMethods;
import com.google.gwt.maps.client.overlays.overlayhandlers.OverlayViewOnDrawHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
@@ -66,6 +70,8 @@ public class PathPolyline {
private List<SimulatorWindDTO> allPoints = null;
private SimulatorServiceAsync simulatorService = null;
private MapWidget map = null;
protected OverlayView mapView;
protected MapCanvasProjection mapProjection;
private ErrorReporter errorReporter = null;
private boolean warningAlreadyShown = false;
private SimulatorMap simulatorMap = null;
@@ -114,6 +120,12 @@ public class PathPolyline {
this.allPoints = pathPoints;
this.simulatorService = simulatorService;
this.map = map;
this.mapView = OverlayView.newInstance(map, new OverlayViewOnDrawHandler() {
@Override
public void onDraw(OverlayViewMethods methods) {
mapProjection = methods.getProjection();
}
}, null, null);
this.selectedBoatClassIndex = selection.boatClassIndex;
this.selectedRaceIndex = selection.raceIndex;
this.selectedCompetitorIndex = selection.competitorIndex;
@@ -234,7 +246,7 @@ public class PathPolyline {
simulatorMap.setPolyline(polyline);
polyline.setEditable(true);
// old call: polyline.setEditingEnabled(PolyEditingOptions.newInstance(turnPoints.length - 1));
getTotalTime();
}
@@ -1037,7 +1049,7 @@ public class PathPolyline {
/**
* Converts a LatLng object to a TwoD object. Considering that a LatLng represent a point on a sphere, and that the
* TwoDPoint represent one on a plane, it will use the MapWidget aproximation to a container pixel in order to do
* TwoDPoint represent one on a plane, it will use the MapWidget approximation to a container pixel in order to do
* so.
*
* @param latLng
@@ -1045,8 +1057,7 @@ public class PathPolyline {
* @returns a TwoDPoint object.
*/
private TwoDPoint toTwoDPoint(LatLng latLng) {
// TODO MigrationV3: Point point = map.convertLatLngToContainerPixel(latLng);
Point point = Point.newInstance(latLng.getLatitude(), latLng.getLongitude());
Point point = this.mapProjection.fromLatLngToContainerPixel(latLng);
return new TwoDPoint(point.getX(), point.getY());
}
@@ -1065,8 +1076,7 @@ public class PathPolyline {
* @returns a LatLng object.
*/
private LatLng toLatLng(TwoDPoint point) {
return LatLng.newInstance((int) point.getX(), (int) point.getY());
// TODO MigrationV3: return this.map.convertContainerPixelToLatLng(Point.newInstance((int) point.getX(), (int) point.getY()));
return this.mapProjection.fromContainerPixelToLatLng(Point.newInstance((int) point.getX(), (int) point.getY()));
}
/**
@@ -148,8 +148,10 @@ public class RaceCourseCanvasOverlay extends FullCanvasOverlay {
if (startPoint != null) {
Point point = mapProjection.fromLatLngToDivPixel(startPoint);
drawCircleWithText(point.getX() - getWidgetPosLeft(), point.getY() - getWidgetPosTop(), racecourseBuoySize,
racecourseColor, "Start");
if (racecourseBuoySize > 0.0) {
drawCircleWithText(point.getX() - getWidgetPosLeft(), point.getY() - getWidgetPosTop(), racecourseBuoySize,
racecourseColor, "Start");
}
if (startMarker != null) {
startMarker.setPosition(startPoint);
} else {
@@ -174,10 +176,11 @@ public class RaceCourseCanvasOverlay extends FullCanvasOverlay {
}
if (endPoint != null) {
Point point = mapProjection.fromLatLngToDivPixel(endPoint);
drawCircleWithText(point.getX() - getWidgetPosLeft(), point.getY() - getWidgetPosTop(), racecourseBuoySize,
racecourseColor, "End");
Point point = mapProjection.fromLatLngToDivPixel(endPoint);
if (racecourseBuoySize > 0.0) {
drawCircleWithText(point.getX() - getWidgetPosLeft(), point.getY() - getWidgetPosTop(), racecourseBuoySize,
racecourseColor, "End");
}
if (endMarker != null) {
endMarker.setPosition(endPoint);
} else {
@@ -5,12 +5,6 @@ import com.google.gwt.canvas.dom.client.Context2d.TextAlign;
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.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;
import com.google.gwt.maps.client.events.idle.IdleMapHandler;
import com.google.gwt.maps.client.overlays.Circle;
import com.google.gwt.maps.client.overlays.CircleOptions;
import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay;
import com.sap.sailing.simulator.util.SailingSimulatorConstants;
@@ -22,7 +16,6 @@ public class RegattaAreaCanvasOverlay extends FullCanvasOverlay {
private CourseAreaDescriptor currentCourseArea = null;
private boolean initial;
private boolean mapPan = false;
private double raceBearing = 0.0;
private double diffBearing = 0.0;
@@ -32,58 +25,7 @@ public class RegattaAreaCanvasOverlay extends FullCanvasOverlay {
this.simulatorMap = simulatorMap;
this.venue = VenueDescriptorFactory.createVenue(event);
this.currentCourseArea = this.venue.getDefaultCourseArea();
/*getMap().addZoomChangeHandler(new ZoomChangeMapHandler() {
@Override
public void onEvent(ZoomChangeMapEvent event) {
// TODO Auto-generated method stub
simulatorMap.windRoseCanvasOverlay.removeFromMap();
simulatorMap.windNeedleCanvasOverlay.removeFromMap();
mapPan = true;
}
});
getMap().addDragStartHandler(new DragStartMapHandler() {
@Override
public void onEvent(DragStartMapEvent event) {
// TODO Auto-generated method stub
simulatorMap.windRoseCanvasOverlay.removeFromMap();
simulatorMap.windNeedleCanvasOverlay.removeFromMap();
}
});
getMap().addDragEndHandler(new DragEndMapHandler() {
@Override
public void onEvent(DragEndMapEvent event) {
// TODO Auto-generated method stub
simulatorMap.windRoseCanvasOverlay.addToMap();
simulatorMap.windNeedleCanvasOverlay.addToMap();
}
});*/
getMap().addIdleHandler(new IdleMapHandler() {
@Override
public void onEvent(IdleMapEvent event) {
// TODO Auto-generated method stub
if (mapPan) {
//System.out.println("Map Idle Event Handler.");
simulatorMap.getWindRoseCanvasOverlay().addToMap();
simulatorMap.getWindNeedleCanvasOverlay().addToMap();
mapPan = false;
}
}
});
this.currentCourseArea = this.venue.getDefaultCourseArea();
}
@@ -93,7 +35,7 @@ public class RegattaAreaCanvasOverlay extends FullCanvasOverlay {
int counter = 0;
for (CourseAreaDescriptor courseArea : venue.getCourseAreas()) {
drawCircleFromRadius(counter++, courseArea);
simulatorMap.drawCircleFromRadius(counter++, courseArea);
}
map.panTo(venue.getCenterPos());
@@ -142,48 +84,6 @@ public class RegattaAreaCanvasOverlay extends FullCanvasOverlay {
}
}
// TODO MigrationV3: This is not the right place to create Circles being not drawn to the canvas -> move to SimulatorMap
public void drawCircleFromRadius(int regIdx, CourseAreaDescriptor courseArea) {
CircleOptions circleOptions = CircleOptions.newInstance();
circleOptions.setStrokeColor("white");
circleOptions.setStrokeWeight(1);
circleOptions.setStrokeOpacity(0.0);
circleOptions.setFillColor("green");
circleOptions.setFillOpacity(0.0);
circleOptions.setCenter(courseArea.getCenterPos());
Circle circle = Circle.newInstance(circleOptions);
circle.setRadius(courseArea.getRadiusInMeters());
final int regIdxFinal = regIdx;
circle.addClickHandler(new ClickMapHandler() {
public void onEvent(ClickMapEvent e) {
//System.out.println("Click: "+currentCourseArea.getName());
CourseAreaDescriptor newRegArea = venue.getCourseAreas().get(regIdxFinal);
if (newRegArea != currentCourseArea) {
currentCourseArea = newRegArea;
simulatorMap.clearOverlays();
simulatorMap.getWindRoseCanvasOverlay().removeFromMap();
simulatorMap.getWindNeedleCanvasOverlay().removeFromMap();
updateRaceCourse(0, 0);
raceCourseCanvasOverlay.draw();
}
map.panTo(currentCourseArea.getCenterPos());
mapPan = true;
if (map.getZoom() < 14) {
map.setZoom(14);
}
}
});
circle.setMap(getMap());
}
protected void updateRaceCourse(int type, double bearing) {
if (type == 1) {
@@ -209,6 +109,14 @@ public class RegattaAreaCanvasOverlay extends FullCanvasOverlay {
}
}
public CourseAreaDescriptor getCurrentCourseArea() {
return this.currentCourseArea;
}
public void setCurrentCourseArea(CourseAreaDescriptor courseArea) {
this.currentCourseArea = courseArea;
}
protected void drawCourseArea(String name, Context2d context2d, Point centerPoint, Point borderPoint,
String color, String colorText, double pxStroke) {
@@ -216,18 +124,23 @@ public class RegattaAreaCanvasOverlay extends FullCanvasOverlay {
centerPoint.getY() - borderPoint.getY());
double pxRadius = Math.sqrt(diffPoint.getX() * diffPoint.getX() + diffPoint.getY() * diffPoint.getY());
String bgColor;
if (simulatorMap.getWindParams().isShowStreamlets2()) {
bgColor = "#505050";
} else {
bgColor = "#DEDEDE";
}
context2d.setGlobalAlpha(1.0f);
context2d.setFillStyle("#DEDEDE");
context2d.setLineWidth(pxStroke);
context2d.setStrokeStyle(color);
context2d.setFillStyle(bgColor);
context2d.beginPath();
context2d.arc(centerPoint.getX() - this.getWidgetPosLeft(), centerPoint.getY() - this.getWidgetPosTop(),
pxRadius, 0.0, 2 * Math.PI);
context2d.closePath();
context2d.fill();
context2d.stroke();
context2d.setGlobalAlpha(0.4f);
context2d.setGlobalAlpha(0.5f);
context2d.setFillStyle(color);
context2d.beginPath();
context2d.arc(centerPoint.getX() - this.getWidgetPosLeft(), centerPoint.getY() - this.getWidgetPosTop(),
@@ -235,6 +148,24 @@ public class RegattaAreaCanvasOverlay extends FullCanvasOverlay {
context2d.closePath();
context2d.fill();
context2d.setGlobalAlpha(1.0f);
context2d.setLineWidth(pxStroke);
context2d.setStrokeStyle(bgColor);
context2d.beginPath();
context2d.arc(centerPoint.getX() - this.getWidgetPosLeft(), centerPoint.getY() - this.getWidgetPosTop(),
pxRadius, 0.0, 2 * Math.PI);
context2d.closePath();
context2d.stroke();
context2d.setGlobalAlpha(0.8f);
context2d.setLineWidth(pxStroke);
context2d.setStrokeStyle(color);
context2d.beginPath();
context2d.arc(centerPoint.getX() - this.getWidgetPosLeft(), centerPoint.getY() - this.getWidgetPosTop(),
pxRadius, 0.0, 2 * Math.PI);
context2d.closePath();
context2d.stroke();
if (getMap().getZoom() >= 11) {
context2d.setGlobalAlpha(0.8f);
context2d.setFillStyle(colorText);
@@ -246,9 +177,9 @@ public class RegattaAreaCanvasOverlay extends FullCanvasOverlay {
context2d.fillText(name, centerPoint.getX() - this.getWidgetPosLeft(), centerPoint.getY() + 0.32 * fontsize
- this.getWidgetPosTop());
context2d.setGlobalAlpha(1.0f);
}
context2d.setGlobalAlpha(1.0f);
}
protected LatLng getEdgePoint(LatLng pos, double dist) {
@@ -282,4 +213,8 @@ public class RegattaAreaCanvasOverlay extends FullCanvasOverlay {
this.raceCourseCanvasOverlay = rcCanvas;
}
public VenueDescriptor getVenue() {
return this.venue;
}
}
@@ -10,6 +10,7 @@ import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.sap.sailing.gwt.ui.client.AbstractEntryPoint;
import com.sap.sailing.gwt.ui.client.JavaScriptInjector;
import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel;
import com.sap.sailing.gwt.ui.client.RemoteServiceMappingConstants;
import com.sap.sailing.gwt.ui.client.SimulatorService;
@@ -32,16 +33,25 @@ public class SimulatorEntryPoint extends AbstractEntryPoint {
private boolean showLines = false; // show the wind lines in the wind display and replay modes.
private char seedLines = 'b'; // seed lines at: 'b'ack, 'f'ront
private boolean showStreamlets = true; // show the wind streamlets in the wind display and replay modes.
private boolean showStreamlets2 = false; // show animated wind streamlets in the wind display and replay modes.
private boolean injectWindDataJS = false;
private static Logger logger = Logger.getLogger(SimulatorEntryPoint.class.getName());
@Override
protected void doOnModuleLoad() {
super.doOnModuleLoad();
super.doOnModuleLoad();
registerASyncService((ServiceDefTarget) simulatorService, RemoteServiceMappingConstants.simulatorServiceRemotePath);
checkUrlParameters();
checkUrlParameters();
if (this.injectWindDataJS) {
SimulatorJSBundle bundle = GWT.create(SimulatorJSBundle.class);
JavaScriptInjector.inject(bundle.windStreamletsDataJS().getText());
}
createSimulatorPanel();
}
@@ -106,6 +116,15 @@ public class SimulatorEntryPoint extends AbstractEntryPoint {
} else {
showStreamlets = false;
}
if (windDisplayStr.contains("z")) {
showStreamlets2 = true;
} else {
showStreamlets2 = false;
}
if (windDisplayStr.contains("y")) {
showStreamlets2 = true;
injectWindDataJS = true;
}
if (windDisplayStr.contains("b")) {
seedLines = 'b';
}
@@ -135,7 +154,7 @@ public class SimulatorEntryPoint extends AbstractEntryPoint {
private void createSimulatorPanel() {
SimulatorMainPanel simulatorPanel = new SimulatorMainPanel(simulatorService, stringMessages, this, xRes, yRes,
autoUpdate, mode, event, showGrid, showLines, seedLines, showArrows, showStreamlets);
autoUpdate, mode, event, showGrid, showLines, seedLines, showArrows, showStreamlets, showStreamlets2, injectWindDataJS);
DockLayoutPanel p = new DockLayoutPanel(Unit.PX);
RootLayoutPanel.get().add(p);
@@ -0,0 +1,14 @@
package com.sap.sailing.gwt.ui.simulator;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.TextResource;
public interface SimulatorJSBundle extends ClientBundle {
//@Source("wind-streamlets.js")
//TextResource windStreamletsJS();
@Source("com/sap/sailing/gwt/ui/simulator/wind-data.js")
TextResource windStreamletsDataJS();
}
@@ -71,7 +71,7 @@ public class SimulatorMainPanel extends SimplePanel {
simulatorMap.getMap().triggerResize();
}
}
private DockLayoutPanel mainPanel;
private FlowPanel leftPanel;
private ResizableFlowPanel rightPanel;
@@ -122,6 +122,7 @@ public class SimulatorMainPanel extends SimplePanel {
private CheckBox isPathPolylineFreeMode;
private Chart chart;
private boolean injectWindDataJS = false;
private boolean warningAlreadyShown = false;
private DialogBox polarDiagramDialogBox;
private Button polarDiagramDialogCloseButton;
@@ -225,7 +226,7 @@ public class SimulatorMainPanel extends SimplePanel {
}
public SimulatorMainPanel(SimulatorServiceAsync svc, StringMessages stringMessages, ErrorReporter errorReporter, int xRes, int yRes, boolean autoUpdate,
char mode, char event, boolean showGrid, boolean showLines, char seedLines, boolean showArrows, boolean showStreamlets) {
char mode, char event, boolean showGrid, boolean showLines, char seedLines, boolean showArrows, boolean showStreamlets, boolean showStreamlets2, boolean injectWindDataJS) {
super();
this.simulatorSvc = svc;
@@ -293,8 +294,10 @@ public class SimulatorMainPanel extends SimplePanel {
windParams.setShowLines(showLines);
windParams.setSeedLines(seedLines);
windParams.setShowStreamlets(showStreamlets);
windParams.setShowStreamlets2(showStreamlets2);
this.setDefaultTimeSettings();
this.injectWindDataJS = injectWindDataJS;
timer = new Timer(PlayModes.Replay, 1000l);
TimeRangeWithZoomProvider timeRangeProvider = new TimeRangeWithZoomModel();
initTimer();
@@ -312,7 +315,7 @@ public class SimulatorMainPanel extends SimplePanel {
simulatorMap.setSize("100%", "100%");
this.rightPanel.add(this.simulatorMap);
createOptionsPanelTop();
createOptionsPanel();
@@ -333,6 +336,23 @@ public class SimulatorMainPanel extends SimplePanel {
this.polarDiagramDialogBox = this.createPolarDiagramDialogBox();
}
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 void setDefaultTimeSettings() {
Date defaultNow = new Date();
DateTimeFormat fmt = DateTimeFormat.getFormat("Z");
@@ -464,6 +484,10 @@ public class SimulatorMainPanel extends SimplePanel {
return windControlPanel;
}
public WindPatternDisplay getWindPatternDisplay() {
return currentWPDisplay;
}
private Panel getSliderPanel(Panel parentPanel, WindPatternSetting<?> s) {
String labelName = s.getDisplayName();
@@ -823,36 +847,53 @@ public class SimulatorMainPanel extends SimplePanel {
private void update() {
// int selectedBoatClassIndex = boatClassSelector.getSelectedIndex();
// int selectedRaceIndex = raceSelector.getSelectedIndex();
// int selectedCompetitorIndex = competitorSelector.getSelectedIndex();
int selectedLegIndex = legSelector.getSelectedIndex();
if (this.windParams.isShowStreamlets2()) {
SimulatorUISelectionDTO selection = new SimulatorUISelectionDTO(boatClassSelector.getSelectedIndex(), raceSelector.getSelectedIndex(),
competitorSelector.getSelectedIndex(), legSelector.getSelectedIndex());
this.setMapInstance(this.simulatorMap.getMap().getJso());
this.setCanvasProjectionInstance(this.simulatorMap.getRegattaAreaCanvasOverlay().getMapProjection());
if (windDisplayButton.getValue()) {
showTimePanel(true);
simulatorMap.refreshView(SimulatorMap.ViewName.WINDDISPLAY, currentWPDisplay, selection, true);
} else if (summaryButton.getValue()) {
showTimePanel(false);
simulatorMap.getRegattaAreaCanvasOverlay().draw();
simulatorMap.refreshView(SimulatorMap.ViewName.SUMMARY, currentWPDisplay, selection, true);
} else if (replayButton.getValue()) {
showTimePanel(true);
simulatorMap.refreshView(SimulatorMap.ViewName.REPLAY, currentWPDisplay, selection, true);
} else {
if (mode == SailingSimulatorConstants.ModeMeasured) {
//this.setDefaultTimeSettings();
//this.simulatorMap.generateWindField(this.getWindPatternDisplay(), true);
if (selectedLegIndex % 2 != 0) {
errorReporter.reportError("Downwind legs are NOT supported yet. Sorry about that :)");
} else {
simulatorMap.removePolyline();
showTimePanel(false);
simulatorMap.refreshView(SimulatorMap.ViewName.SUMMARY, currentWPDisplay, selection, true);
}
}
}
}
if (this.injectWindDataJS) {
this.startStreamlets();
} else {
// int selectedBoatClassIndex = boatClassSelector.getSelectedIndex();
// int selectedRaceIndex = raceSelector.getSelectedIndex();
// int selectedCompetitorIndex = competitorSelector.getSelectedIndex();
int selectedLegIndex = legSelector.getSelectedIndex();
SimulatorUISelectionDTO selection = new SimulatorUISelectionDTO(boatClassSelector.getSelectedIndex(), raceSelector.getSelectedIndex(),
competitorSelector.getSelectedIndex(), legSelector.getSelectedIndex());
if (windDisplayButton.getValue()) {
showTimePanel(true);
simulatorMap.refreshView(SimulatorMap.ViewName.WINDDISPLAY, currentWPDisplay, selection, true);
} else if (summaryButton.getValue()) {
showTimePanel(false);
simulatorMap.getRegattaAreaCanvasOverlay().draw();
simulatorMap.refreshView(SimulatorMap.ViewName.SUMMARY, currentWPDisplay, selection, true);
} else if (replayButton.getValue()) {
showTimePanel(true);
simulatorMap.refreshView(SimulatorMap.ViewName.REPLAY, currentWPDisplay, selection, true);
} else {
if (mode == SailingSimulatorConstants.ModeMeasured) {
if (selectedLegIndex % 2 != 0) {
errorReporter.reportError("Downwind legs are NOT supported yet. Sorry about that :)");
} else {
simulatorMap.removePolyline();
showTimePanel(false);
simulatorMap.refreshView(SimulatorMap.ViewName.SUMMARY, currentWPDisplay, selection, true);
}
}
}
}
}
private void initPolarDiagramButton() {
@@ -19,7 +19,15 @@ 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.click.ClickMapEvent;
import com.google.gwt.maps.client.events.click.ClickMapHandler;
import com.google.gwt.maps.client.events.idle.IdleMapEvent;
import com.google.gwt.maps.client.events.idle.IdleMapHandler;
import com.google.gwt.maps.client.maptypes.MapTypeStyleElementType;
import com.google.gwt.maps.client.maptypes.MapTypeStyleFeatureType;
import com.google.gwt.maps.client.maptypes.MapTypeStyler;
import com.google.gwt.maps.client.overlays.Circle;
import com.google.gwt.maps.client.overlays.CircleOptions;
import com.google.gwt.maps.client.overlays.Polyline;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
@@ -58,17 +66,19 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
private static SimulatorResources resources = GWT.create(SimulatorResources.class);
private MapWidget map;
private boolean mapPan = false;
private boolean dataInitialized;
private boolean overlaysInitialized;
private WindFieldGenParamsDTO windParams;
// the canvas overlays
private WindStreamletsCanvasOverlay windStreamletsCanvasOverlay;
private WindLineGuidesCanvasOverlay windLineGuidesCanvasOverlay;
private WindFieldCanvasOverlay windFieldCanvasOverlay;
private WindGridCanvasOverlay windGridCanvasOverlay;
private WindLineCanvasOverlay windLineCanvasOverlay;
private RegattaAreaCanvasOverlay regattaAreaCanvasOverlay;
private WindStreamletsCanvasOverlay windStreamletsCanvasOverlay;
private ImageCanvasOverlay windRoseCanvasOverlay;
private ImageCanvasOverlay windNeedleCanvasOverlay;
private List<PathCanvasOverlay> replayPathCanvasOverlays;
@@ -205,7 +215,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
* Now we always get the wind field
*/
WindFieldDTO windFieldDTO = result.getWindField();
LOGGER.info("Number of windDTO : " + windFieldDTO.getMatrix().size());
//LOGGER.info("Number of windDTO : " + windFieldDTO.getMatrix().size());
if (windParams.isShowGrid()) {
windGridCanvasOverlay.addToMap();
@@ -217,6 +227,9 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
windFieldCanvasOverlay.addToMap();
}
if (windParams.isShowStreamlets()) {
windLineGuidesCanvasOverlay.addToMap();
}
if (windParams.isShowStreamlets2()) {
windStreamletsCanvasOverlay.addToMap();
}
@@ -228,6 +241,9 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
timeListeners.add(windFieldCanvasOverlay);
}
if (windParams.isShowStreamlets()) {
timeListeners.add(windLineGuidesCanvasOverlay);
}
if (windParams.isShowStreamlets2()) {
timeListeners.add(windStreamletsCanvasOverlay);
}
if (windParams.isShowGrid()) {
@@ -244,8 +260,8 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
if (windFieldCanvasOverlay != null) {
windFieldCanvasOverlay.setVisible(false);
}
if (windStreamletsCanvasOverlay != null) {
windStreamletsCanvasOverlay.setVisible(false);
if (windLineGuidesCanvasOverlay != null) {
windLineGuidesCanvasOverlay.setVisible(false);
}
if (windGridCanvasOverlay != null) {
windGridCanvasOverlay.setVisible(false);
@@ -253,6 +269,13 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
if (windLineCanvasOverlay != null) {
windLineCanvasOverlay.setVisible(false);
}
if (windStreamletsCanvasOverlay != null) {
windStreamletsCanvasOverlay.setVisible(false);
}
} else {
if (windStreamletsCanvasOverlay != null) {
windStreamletsCanvasOverlay.setVisible(true);
}
}
legendCanvasOverlay.addToMap();
@@ -282,7 +305,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
this.dataInitialized = false;
this.overlaysInitialized = false;
this.windFieldCanvasOverlay = null;
this.windStreamletsCanvasOverlay = null;
this.windLineGuidesCanvasOverlay = null;
this.windGridCanvasOverlay = null;
this.windLineCanvasOverlay = null;
this.replayPathCanvasOverlays = null;
@@ -309,7 +332,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
this.dataInitialized = false;
this.overlaysInitialized = false;
this.windFieldCanvasOverlay = null;
this.windStreamletsCanvasOverlay = null;
this.windLineGuidesCanvasOverlay = null;
this.windGridCanvasOverlay = null;
this.windLineCanvasOverlay = null;
this.replayPathCanvasOverlays = null;
@@ -338,16 +361,45 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
mapOptions.setRotateControl(true);
mapOptions.setStreetViewControl(false);
MapTypeStyle[] mapTypeStyles = new MapTypeStyle[4];
MapTypeStyle[] mapTypeStyles;
// hide all transit lines including ferry lines
mapTypeStyles[0] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.TRANSIT);
// hide points of interest
mapTypeStyles[1] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.POI);
// simplify road display
mapTypeStyles[2] = GoogleMapStyleHelper.createSimplifiedStyle(MapTypeStyleFeatureType.ROAD);
// set water color
mapTypeStyles[3] = GoogleMapStyleHelper.createColorStyle(MapTypeStyleFeatureType.WATER, new RGBColor(0, 136, 255), -35, -34);
if (windParams.isShowStreamlets2()) {
mapTypeStyles = new MapTypeStyle[7];
// hide all transit lines including ferry lines
mapTypeStyles[0] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.TRANSIT);
// hide points of interest
mapTypeStyles[1] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.POI);
// simplify road display
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);
MapTypeStyle mapStyle = MapTypeStyle.newInstance();
mapStyle.setFeatureType(MapTypeStyleFeatureType.ADMINISTRATIVE);
mapStyle.setElementType(MapTypeStyleElementType.LABELS);
MapTypeStyler[] typeStylers = new MapTypeStyler[1];
typeStylers[0] = MapTypeStyler.newInvertLightnessStyler(true);
mapStyle.setStylers(typeStylers);
mapTypeStyles[6] = mapStyle;
} else {
mapTypeStyles = new MapTypeStyle[4];
// hide all transit lines including ferry lines
mapTypeStyles[0] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.TRANSIT);
// hide points of interest
mapTypeStyles[1] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.POI);
// simplify road display
mapTypeStyles[2] = GoogleMapStyleHelper.createSimplifiedStyle(MapTypeStyleFeatureType.ROAD);
// set water color
mapTypeStyles[3] = GoogleMapStyleHelper.createColorStyle(MapTypeStyleFeatureType.WATER, new RGBColor(0, 136, 255), -35, -34);
}
mapOptions.setMapTypeStyles(mapTypeStyles);
@@ -376,7 +428,56 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
add(map, 0, 0);
map.setSize("100%", "100%");
/*map.addZoomChangeHandler(new ZoomChangeMapHandler() {
@Override
public void onEvent(ZoomChangeMapEvent event) {
// TODO Auto-generated method stub
windRoseCanvasOverlay.removeFromMap();
windNeedleCanvasOverlay.removeFromMap();
mapPan = true;
}
});
map.addDragStartHandler(new DragStartMapHandler() {
@Override
public void onEvent(DragStartMapEvent event) {
// TODO Auto-generated method stub
windRoseCanvasOverlay.removeFromMap();
windNeedleCanvasOverlay.removeFromMap();
}
});
map.addDragEndHandler(new DragEndMapHandler() {
@Override
public void onEvent(DragEndMapEvent event) {
// TODO Auto-generated method stub
windRoseCanvasOverlay.addToMap();
windNeedleCanvasOverlay.addToMap();
}
});*/
map.addIdleHandler(new IdleMapHandler() {
@Override
public void onEvent(IdleMapEvent event) {
// TODO Auto-generated method stub
if (mapPan) {
//System.out.println("Map Idle Event Handler.");
getWindRoseCanvasOverlay().addToMap();
getWindNeedleCanvasOverlay().addToMap();
mapPan = false;
}
}
});
initializeOverlays();
dataInitialized = true;
@@ -420,7 +521,10 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
windFieldCanvasOverlay = new WindFieldCanvasOverlay(map, SimulatorMapOverlaysZIndexes.WINDFIELD_ZINDEX, timer, windParams);
}
if (windParams.isShowStreamlets()) {
windStreamletsCanvasOverlay = new WindStreamletsCanvasOverlay(map, SimulatorMapOverlaysZIndexes.WINDSTREAMLETS_ZINDEX, timer, xRes);
windLineGuidesCanvasOverlay = new WindLineGuidesCanvasOverlay(map, SimulatorMapOverlaysZIndexes.WINDSTREAMLETS_ZINDEX, timer, xRes);
}
if (windParams.isShowStreamlets2()) {
windStreamletsCanvasOverlay = new WindStreamletsCanvasOverlay(this, SimulatorMapOverlaysZIndexes.WINDFIELD_ZINDEX, timer, windParams);
}
if (windParams.isShowGrid()) {
windGridCanvasOverlay = new WindGridCanvasOverlay(map, SimulatorMapOverlaysZIndexes.WINDGRID_ZINDEX, timer, xRes, yRes);
@@ -443,8 +547,8 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
if (windFieldCanvasOverlay != null) {
windFieldCanvasOverlay.onResize();
}
if (windStreamletsCanvasOverlay != null) {
windStreamletsCanvasOverlay.onResize();
if (windLineGuidesCanvasOverlay != null) {
windLineGuidesCanvasOverlay.onResize();
}
if (windGridCanvasOverlay != null) {
windGridCanvasOverlay.onResize();
@@ -461,12 +565,17 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
overlaysInitialized = true;
}
private void generateWindField(final WindPatternDisplay windPatternDisplay, final boolean removeOverlays) {
public void generateWindField(final WindPatternDisplay windPatternDisplay, final boolean removeOverlays) {
LOGGER.info("In generateWindField");
if (windPatternDisplay == null) {
errorReporter.reportError("Please select a valid wind pattern.");
return;
}
if ((windStreamletsCanvasOverlay != null)&&(windStreamletsCanvasOverlay.isVisible())) {
windStreamletsCanvasOverlay.setVisible(false);
}
PositionDTO startPointDTO = new PositionDTO(raceCourseCanvasOverlay.getStartPoint().getLatitude(),
raceCourseCanvasOverlay.getStartPoint().getLongitude());
PositionDTO endPointDTO = new PositionDTO(raceCourseCanvasOverlay.getEndPoint().getLatitude(),
@@ -490,7 +599,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
if (removeOverlays) {
removeOverlays();
}
LOGGER.info("Number of windDTO : " + wl.getMatrix().size());
//LOGGER.info("Number of windDTO : " + wl.getMatrix().size());
// Window.alert("Number of windDTO : " + wl.getMatrix().size());
if (windParams.isShowGrid()) {
windGridCanvasOverlay.addToMap();
@@ -502,6 +611,9 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
windFieldCanvasOverlay.addToMap();
}
if (windParams.isShowStreamlets()) {
windLineGuidesCanvasOverlay.addToMap();
}
if (windParams.isShowStreamlets2()) {
windStreamletsCanvasOverlay.addToMap();
}
refreshWindFieldOverlay(wl);
@@ -510,7 +622,11 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
timeListeners.add(windFieldCanvasOverlay);
}
if (windParams.isShowStreamlets()) {
timeListeners.add(windLineGuidesCanvasOverlay);
}
if (windParams.isShowStreamlets2()) {
timeListeners.add(windStreamletsCanvasOverlay);
windStreamletsCanvasOverlay.setVisible(true);
}
if (windParams.isShowGrid()) {
timeListeners.add(windGridCanvasOverlay);
@@ -528,11 +644,14 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
}
private void refreshWindFieldOverlay(WindFieldDTO windFieldDTO) {
if (this.windStreamletsCanvasOverlay != null) {
this.windStreamletsCanvasOverlay.setWindField(windFieldDTO);
}
if (this.windFieldCanvasOverlay != null) {
this.windFieldCanvasOverlay.setWindField(windFieldDTO);
}
if (this.windStreamletsCanvasOverlay != null) {
this.windStreamletsCanvasOverlay.setWindField(windFieldDTO);
if (this.windLineGuidesCanvasOverlay != null) {
this.windLineGuidesCanvasOverlay.setWindField(windFieldDTO);
}
if (this.windGridCanvasOverlay != null) {
this.windGridCanvasOverlay.setWindField(windFieldDTO);
@@ -549,7 +668,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
this.windFieldCanvasOverlay.draw();
}
if (this.windParams.isShowStreamlets()) {
this.windStreamletsCanvasOverlay.draw();
this.windLineGuidesCanvasOverlay.draw();
}
if (this.windParams.isShowGrid()) {
this.windGridCanvasOverlay.draw();
@@ -567,6 +686,10 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
return;
}
if ((windStreamletsCanvasOverlay != null)&&(windStreamletsCanvasOverlay.isVisible())) {
windStreamletsCanvasOverlay.setVisible(false);
}
if (mode != SailingSimulatorConstants.ModeMeasured) {
PositionDTO startPointDTO = new PositionDTO(raceCourseCanvasOverlay.getStartPoint().getLatitude(),
raceCourseCanvasOverlay.getStartPoint().getLongitude());
@@ -611,6 +734,10 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
windFieldCanvasOverlay.removeFromMap();
num++;
}
if (windLineGuidesCanvasOverlay != null) {
windLineGuidesCanvasOverlay.removeFromMap();
num++;
}
if (windStreamletsCanvasOverlay != null) {
windStreamletsCanvasOverlay.removeFromMap();
num++;
@@ -656,6 +783,9 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
if (windStreamletsCanvasOverlay != null) {
windStreamletsCanvasOverlay.setVisible(false);
}
if (windLineGuidesCanvasOverlay != null) {
windLineGuidesCanvasOverlay.setVisible(false);
}
if (windGridCanvasOverlay != null) {
windGridCanvasOverlay.setVisible(false);
}
@@ -692,7 +822,10 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
if (windFieldCanvasOverlay != null) {
windFieldCanvasOverlay.setVisible(true);
}
if (windStreamletsCanvasOverlay != null) {
if (windLineGuidesCanvasOverlay != null) {
windLineGuidesCanvasOverlay.setVisible(true);
}
if ((windStreamletsCanvasOverlay != null)&&(!windStreamletsCanvasOverlay.isVisible())) {
windStreamletsCanvasOverlay.setVisible(true);
}
if (windGridCanvasOverlay != null) {
@@ -731,9 +864,13 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
windFieldCanvasOverlay.setVisible(true);
windFieldCanvasOverlay.draw();
}
if (windStreamletsCanvasOverlay != null) {
if (windLineGuidesCanvasOverlay != null) {
windLineGuidesCanvasOverlay.setVisible(true);
windLineGuidesCanvasOverlay.draw();
}
if ((windStreamletsCanvasOverlay != null)&&(!windStreamletsCanvasOverlay.isVisible())) {
windStreamletsCanvasOverlay.setVisible(true);
windStreamletsCanvasOverlay.draw();
//windStreamletsCanvasOverlay.draw();
}
if (windGridCanvasOverlay != null) {
windGridCanvasOverlay.setVisible(true);
@@ -773,6 +910,9 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
}
switch (name) {
case SUMMARY:
if ((windStreamletsCanvasOverlay != null)&&(windStreamletsCanvasOverlay.isVisible())) {
windStreamletsCanvasOverlay.setVisible(false);
}
refreshSummaryView(windPatternDisplay, selection, force);
break;
case REPLAY:
@@ -889,5 +1029,44 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ
public RaceCourseCanvasOverlay getRaceCourseCanvasOverlay() {
return raceCourseCanvasOverlay;
}
public void drawCircleFromRadius(int regIdx, CourseAreaDescriptor courseArea) {
CircleOptions circleOptions = CircleOptions.newInstance();
circleOptions.setStrokeColor("white");
circleOptions.setStrokeWeight(1);
circleOptions.setStrokeOpacity(0.0);
circleOptions.setFillColor("green");
circleOptions.setFillOpacity(0.0);
circleOptions.setCenter(courseArea.getCenterPos());
Circle circle = Circle.newInstance(circleOptions);
circle.setRadius(courseArea.getRadiusInMeters());
final int regIdxFinal = regIdx;
circle.addClickHandler(new ClickMapHandler() {
public void onEvent(ClickMapEvent e) {
//System.out.println("Click: "+currentCourseArea.getName());
CourseAreaDescriptor newRegArea = regattaAreaCanvasOverlay.getVenue().getCourseAreas().get(regIdxFinal);
if (newRegArea != regattaAreaCanvasOverlay.getCurrentCourseArea()) {
regattaAreaCanvasOverlay.setCurrentCourseArea(newRegArea);
clearOverlays();
getWindRoseCanvasOverlay().removeFromMap();
getWindNeedleCanvasOverlay().removeFromMap();
regattaAreaCanvasOverlay.updateRaceCourse(0, 0);
raceCourseCanvasOverlay.draw();
}
map.panTo(regattaAreaCanvasOverlay.getCurrentCourseArea().getCenterPos());
mapPan = true;
if (map.getZoom() < 14) {
map.setZoom(14);
}
}
});
circle.setMap(getMap());
}
}
@@ -38,15 +38,15 @@ public class VenueDescriptorFactory {
// Foxtrot
cPos = LatLng.newInstance(54.445775, 10.29223889);
courseAreas.add(new CourseAreaDescriptor("Foxtrot", cPos, 0.65, "#B4287C"));
courseAreas.add(new CourseAreaDescriptor("Foxtrot", cPos, 0.65, "#DA58A6"));
// India
cPos = LatLng.newInstance(54.44803611, 10.20863611);
courseAreas.add(new CourseAreaDescriptor("India", cPos, 0.40, "#774741"));
courseAreas.add(new CourseAreaDescriptor("India", cPos, 0.40, "#B7827B"));
// Juliett
cPos = LatLng.newInstance(54.46183611, 10.2239);
courseAreas.add(new CourseAreaDescriptor("Juliett", cPos, 0.55, "#818585"));
courseAreas.add(new CourseAreaDescriptor("Juliett", cPos, 0.55, "#979b9b"));
// Echo
cPos = LatLng.newInstance(54.47640278, 10.20090556);
@@ -58,7 +58,7 @@ public class VenueDescriptorFactory {
// Charlie
cPos = LatLng.newInstance(54.49327222, 10.17525833);
courseAreas.add(new CourseAreaDescriptor("Charlie", cPos, 0.70, "#0A5998"));
courseAreas.add(new CourseAreaDescriptor("Charlie", cPos, 0.70, "#2796f1"));
// Delta
cPos = LatLng.newInstance(54.49706111, 10.21921944);
@@ -66,11 +66,11 @@ public class VenueDescriptorFactory {
// Bravo
cPos = LatLng.newInstance(54.50911667, 10.13973333);
courseAreas.add(new CourseAreaDescriptor("Bravo", cPos, 0.80, "#CE3032"));
courseAreas.add(new CourseAreaDescriptor("Bravo", cPos, 0.80, "#d34547"));
// Alfa
cPos = LatLng.newInstance(54.52905, 10.18515278);
courseAreas.add(new CourseAreaDescriptor("Alfa", cPos, 1.00, "#D9699B"));
courseAreas.add(new CourseAreaDescriptor("Alfa", cPos, 1.00, "#d65c93"));
venue.setDefaultCourseArea(defaultCourseArea);
return venue;
@@ -229,7 +229,7 @@ public class WindLineCanvasOverlay extends FullCanvasOverlay implements TimeList
final double weight = 1.0;
LatLng positionLatLng = LatLng.newInstance(p1.latDeg, p1.lngDeg);
Point canvasPositionInPx = mapProjection.fromLatLngToContainerPixel(positionLatLng);
Point canvasPositionInPx = mapProjection.fromLatLngToDivPixel(positionLatLng);
final double x1 = canvasPositionInPx.getX() - this.getWidgetPosLeft();
final double y1 = canvasPositionInPx.getY() - this.getWidgetPosTop();
@@ -0,0 +1,273 @@
package com.sap.sailing.gwt.ui.simulator;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Logger;
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.impl.DegreeBearingImpl;
import com.sap.sailing.gwt.ui.client.Timer;
import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO;
import com.sap.sailing.gwt.ui.shared.WindFieldDTO;
import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay;
import com.sap.sailing.gwt.ui.simulator.util.ToolTip;
/**
* 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 WindLineGuidesCanvasOverlay extends FullCanvasOverlay implements TimeListenerWithStoppingCriteria {
/** The wind field that is to be displayed in the overlay */
private WindFieldDTO windFieldDTO;
/**
* Map containing the windfield for easy retrieval with key as time point.
*/
private SortedMap<Long, List<SimulatorWindDTO>> timePointWindDTOMap;
/** The points where ToolTip is to be displayed */
private Map<ToolTip, SimulatorWindDTO> windFieldPoints;
private String arrowColor = "Black";
private String arrowHeadColor = "Blue";
private int xRes;
private Timer timer;
private static Logger logger = Logger.getLogger(WindLineGuidesCanvasOverlay.class.getName());
public WindLineGuidesCanvasOverlay(MapWidget map, int zIndex, final Timer timer, int xRes) {
super(map, zIndex);
this.timer = timer;
this.xRes = xRes;
windFieldDTO = null;
windFieldPoints = new HashMap<ToolTip, SimulatorWindDTO>();
timePointWindDTOMap = new TreeMap<Long, List<SimulatorWindDTO>>();
}
public WindLineGuidesCanvasOverlay(MapWidget map, int zIndex) {
this(map, zIndex, null, 0);
}
@Override
public void addToMap() {
super.addToMap();
if (timer != null) {
timer.addTimeListener(this);
}
}
@Override
public void removeFromMap() {
super.removeFromMap();
if (timer != null) {
timer.removeTimeListener(this);
}
}
public void setWindField(final WindFieldDTO windField) {
this.windFieldDTO = windField;
timePointWindDTOMap.clear();
if (windField != null) {
for(final SimulatorWindDTO w : windField.getMatrix()) {
if (!timePointWindDTOMap.containsKey(w.timepoint)) {
timePointWindDTOMap.put(w.timepoint, new LinkedList<SimulatorWindDTO>());
}
timePointWindDTOMap.get(w.timepoint).add(w);
}
}
}
public void setArrowColor(final String arrowColor, final String arrowHeadColor) {
this.arrowColor = arrowColor;
this.arrowHeadColor = arrowHeadColor;
}
@Override
protected void draw() {
super.draw();
if (mapProjection != null && windFieldDTO != null) {
clear();
drawWindField();
}
}
private void clear() {
canvas.getContext2d().clearRect(0.0 /*canvas.getAbsoluteLeft()*/, 0.0/*canvas.getAbsoluteTop()*/,
canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
windFieldPoints.clear();
}
protected void drawWindField() {
if (timer != null) {
timeChanged(timer.getTime(), null);
} else {
drawWindField(windFieldDTO.getMatrix());
}
}
protected void drawScaledArrow(final SimulatorWindDTO windDTO, final double angle, final int index, final double pxLength, final boolean drawHead) {
final double aWidth = 1.0;
drawArrow(windDTO, angle, pxLength, aWidth, index, drawHead);
}
protected void drawWindField(final List<SimulatorWindDTO> windDTOList) {
final boolean drawHead = false;
clear();
final Context2d context2d = canvas.getContext2d();
context2d.setGlobalAlpha(0.4);
if (windDTOList != null && windDTOList.size() > 0) {
Iterator<SimulatorWindDTO> windDTOIter = windDTOList.iterator();
final SimulatorWindDTO w0 = windDTOIter.next();
final SimulatorWindDTO w1 = windDTOIter.next();
final LatLng pg0 = LatLng.newInstance(w0.position.latDeg, w0.position.lngDeg);
final LatLng pg1 = LatLng.newInstance(w1.position.latDeg, w1.position.lngDeg);
final Point px0 = mapProjection.fromLatLngToDivPixel(pg0);
final Point px1 = mapProjection.fromLatLngToDivPixel(pg1);
final double dx = px0.getX()-px1.getX();
final double dy = px0.getY()-px1.getY();
final double pxLength = Math.sqrt( dx*dx + dy*dy );
logger.fine("pxLength = "+pxLength);
windDTOIter = windDTOList.iterator();
int index = 0;
while (windDTOIter.hasNext()) {
SimulatorWindDTO windDTO = windDTOIter.next();
//System.out.println("wind angle: "+index+", "+windDTO.trueWindBearingDeg);
if (((index % xRes) > 0)&&((index % xRes) < (xRes-1))) {
DegreeBearingImpl dbi = new DegreeBearingImpl(windDTO.trueWindBearingDeg);
drawScaledArrow(windDTO, dbi.getRadians(), index, pxLength, drawHead);
}
index++;
}
final String title = "Wind Field at " + windDTOList.size() + " points.";
getCanvas().setTitle(title);
}
}
protected void drawArrow(final SimulatorWindDTO windDTO, final double angle, final double length, final double weight, final int index, final boolean drawHead) {
final String msg = "Wind @ P" + index + ": time : " + windDTO.timepoint + " speed: " + windDTO.trueWindSpeedInKnots + "knots "
+ windDTO.trueWindBearingDeg;
logger.fine(msg);
final Context2d context2d = canvas.getContext2d();
context2d.setGlobalAlpha(0.2);
final PositionDTO position = windDTO.position;
final LatLng positionLatLng = LatLng.newInstance(position.latDeg, position.lngDeg);
final Point canvasPositionInPx = mapProjection.fromLatLngToDivPixel(positionLatLng);
final double x = canvasPositionInPx.getX() - getWidgetPosLeft();
final double y = canvasPositionInPx.getY() - getWidgetPosTop();
windFieldPoints.put(new ToolTip(x, y), windDTO);
final double dx = length * Math.sin(angle);
final double dy = -length * Math.cos(angle);
final double x1 = x + dx / 2;
final double y1 = y + dy / 2;
drawLine(x - dx / 2, y - dy / 2, x1, y1, weight, arrowColor);
final double theta = Math.atan2(-dy, dx);
final double hLength = Math.max(6.,6.+(10./(60.-10.))*Math.max(length-6.,0));
logger.finer("headlength: "+hLength+", arrowlength: "+length);
if (drawHead) {
drawHead(x1, y1, theta, hLength, weight);
}
//String text = "P" + index;// + NumberFormat.getFormat("0.00").format(windDTO.trueWindBearingDeg) + "�";
//drawPointWithText(x, y, text);
//drawPoint(x, y);
context2d.setGlobalAlpha(0.4);
}
protected void drawHead(final double x, final double y, final double theta, final double headLength, final double weight) {
double t = theta + (Math.PI / 4);
if (t > Math.PI) {
t -= 2 * Math.PI;
}
double t2 = theta - (Math.PI / 4);
if (t2 <= (-Math.PI)) {
t2 += 2 * Math.PI;
}
final double x1 = (x - Math.cos(t) * headLength);
final double y1 = (y + Math.sin(t) * headLength);
final double x1o = (x + Math.cos(t) * weight/2);
final double y1o = (y - Math.sin(t) * weight/2);
final double x2 = (x - Math.cos(t2) * headLength);
final double y2 = (y + Math.sin(t2) * headLength);
final double x2o = (x + Math.cos(t2) * weight/2);
final double y2o = (y - Math.sin(t2) * weight/2);
drawLine(x1o, y1o, x1, y1, weight, arrowHeadColor);
drawLine(x2o, y2o, x2, y2, weight, arrowHeadColor);
}
@Override
public void timeChanged(final Date newDate, Date oldDate) {
List<SimulatorWindDTO> windDTOToDraw = new ArrayList<SimulatorWindDTO>();
final SortedMap<Long, List<SimulatorWindDTO>> headMap = (timePointWindDTOMap.headMap(newDate.getTime()+1));
if (!headMap.isEmpty()) {
windDTOToDraw = headMap.get(headMap.lastKey());
}
logger.info("In WindFieldCanvasOverlay.drawWindField drawing " + windDTOToDraw.size() + " points" + " @ "
+ newDate);
drawWindField(windDTOToDraw);
}
@Override
public boolean shallStop() {
if (!this.isVisible() || timePointWindDTOMap == null || timer == null || timePointWindDTOMap.isEmpty()) {
return true;
}
if (timePointWindDTOMap.lastKey() < timer.getTime().getTime()) {
return true;
} else {
return false;
}
}
public void setTimer(final Timer timer) {
this.timer = timer;
}
public Timer getTimer() {
return timer;
}
}
@@ -1,27 +1,20 @@
package com.sap.sailing.gwt.ui.simulator;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Logger;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.maps.client.MapWidget;
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.google.gwt.maps.client.base.Point;
import com.sap.sailing.domain.common.dto.PositionDTO;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.gwt.ui.client.Timer;
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.util.ToolTip;
/**
* A google map overlay based on a HTML5 canvas for drawing a wind field. The overlay covers the whole map and displays
@@ -31,45 +24,214 @@ import com.sap.sailing.gwt.ui.simulator.util.ToolTip;
*
*/
public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements TimeListenerWithStoppingCriteria {
/** The wind field that is to be displayed in the overlay */
private WindFieldDTO windFieldDTO;
/**
* Map containing the windfield for easy retrieval with key as time point.
*/
private SortedMap<Long, List<SimulatorWindDTO>> timePointWindDTOMap;
protected WindFieldDTO windFieldDTO;
protected WindFieldGenParamsDTO windParams = null;
protected SimulatorMap simulatorMap;
/** The points where ToolTip is to be displayed */
private Map<ToolTip, SimulatorWindDTO> windFieldPoints;
private String arrowColor = "Black";
private String arrowHeadColor = "Blue";
private int xRes;
private boolean visible = false;
private Timer timer;
private static Logger logger = Logger.getLogger(WindStreamletsCanvasOverlay.class.getName());
public WindStreamletsCanvasOverlay(MapWidget map, int zIndex, final Timer timer, int xRes) {
super(map, zIndex);
public WindStreamletsCanvasOverlay(SimulatorMap simulatorMap, int zIndex, final Timer timer, final WindFieldGenParamsDTO windParams) {
super(simulatorMap.getMap(), zIndex);
this.simulatorMap = simulatorMap;
this.timer = timer;
this.xRes = xRes;
this.windParams = windParams;
windFieldDTO = null;
windFieldPoints = new HashMap<ToolTip, SimulatorWindDTO>();
getCanvas().getElement().setId("swarm-display");
timePointWindDTOMap = new TreeMap<Long, List<SimulatorWindDTO>>();
/*if (!this.isSwarmDataExt()) {
// random test windfield
JavaScriptObject swarmData = this.getJSONRandomWindData();
this.setSwarmData(swarmData);
}*/
}
public WindStreamletsCanvasOverlay(MapWidget map, int zIndex) {
this(map, zIndex, null, 0);
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(WindStreamletsCanvasOverlay 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 WindStreamletsCanvasOverlay(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();
int steps = data.size() / (this.windParams.getxRes() * this.windParams.getyRes());
String jsonData = "data:[";
int p = 0;
double maxWindSpeed = 0;
for(int s=0; s<steps; s++) {
jsonData +="[";
for(int i=0; i<this.windParams.getyRes(); i++) {
jsonData +="[";
for(int j=0; j<this.windParams.getxRes(); 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<(this.windParams.getxRes()-1)) {
jsonData += ",";
}
}
if (i<(this.windParams.getyRes()-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() {
super.addToMap();
if (timer != null) {
timer.addTimeListener(this);
}
@@ -77,190 +239,41 @@ public class WindStreamletsCanvasOverlay extends FullCanvasOverlay implements Ti
@Override
public void removeFromMap() {
super.removeFromMap();
if (timer != null) {
timer.removeTimeListener(this);
}
this.setVisible(false);
}
@Override
protected void drawCenterChanged() {
}
public void setWindField(final WindFieldDTO windField) {
this.windFieldDTO = windField;
timePointWindDTOMap.clear();
if (windField != null) {
for(final SimulatorWindDTO w : windField.getMatrix()) {
if (!timePointWindDTOMap.containsKey(w.timepoint)) {
timePointWindDTOMap.put(w.timepoint, new LinkedList<SimulatorWindDTO>());
}
timePointWindDTOMap.get(w.timepoint).add(w);
}
}
}
public void setArrowColor(final String arrowColor, final String arrowHeadColor) {
this.arrowColor = arrowColor;
this.arrowHeadColor = arrowHeadColor;
}
@Override
protected void draw() {
super.draw();
if (mapProjection != null && windFieldDTO != null) {
clear();
drawWindField();
// drawing is done by external JavaScript for Streamlets
}
}
private void clear() {
canvas.getContext2d().clearRect(0.0 /*canvas.getAbsoluteLeft()*/, 0.0/*canvas.getAbsoluteTop()*/,
canvas.getCoordinateSpaceWidth(), canvas.getCoordinateSpaceHeight());
windFieldPoints.clear();
}
protected void drawWindField() {
if (timer != null) {
timeChanged(timer.getTime(), null);
} else {
drawWindField(windFieldDTO.getMatrix());
}
}
protected void drawScaledArrow(final SimulatorWindDTO windDTO, final double angle, final int index, final double pxLength, final boolean drawHead) {
final double aWidth = 1.0;
drawArrow(windDTO, angle, pxLength, aWidth, index, drawHead);
}
protected void drawWindField(final List<SimulatorWindDTO> windDTOList) {
final boolean drawHead = false;
clear();
final Context2d context2d = canvas.getContext2d();
context2d.setGlobalAlpha(0.4);
if (windDTOList != null && windDTOList.size() > 0) {
Iterator<SimulatorWindDTO> windDTOIter = windDTOList.iterator();
final SimulatorWindDTO w0 = windDTOIter.next();
final SimulatorWindDTO w1 = windDTOIter.next();
final LatLng pg0 = LatLng.newInstance(w0.position.latDeg, w0.position.lngDeg);
final LatLng pg1 = LatLng.newInstance(w1.position.latDeg, w1.position.lngDeg);
final Point px0 = mapProjection.fromLatLngToDivPixel(pg0);
final Point px1 = mapProjection.fromLatLngToDivPixel(pg1);
final double dx = px0.getX()-px1.getX();
final double dy = px0.getY()-px1.getY();
final double pxLength = Math.sqrt( dx*dx + dy*dy );
logger.fine("pxLength = "+pxLength);
windDTOIter = windDTOList.iterator();
int index = 0;
while (windDTOIter.hasNext()) {
SimulatorWindDTO windDTO = windDTOIter.next();
//System.out.println("wind angle: "+index+", "+windDTO.trueWindBearingDeg);
if (((index % xRes) > 0)&&((index % xRes) < (xRes-1))) {
DegreeBearingImpl dbi = new DegreeBearingImpl(windDTO.trueWindBearingDeg);
drawScaledArrow(windDTO, dbi.getRadians(), index, pxLength, drawHead);
}
index++;
}
final String title = "Wind Field at " + windDTOList.size() + " points.";
getCanvas().setTitle(title);
}
}
protected void drawArrow(final SimulatorWindDTO windDTO, final double angle, final double length, final double weight, final int index, final boolean drawHead) {
final String msg = "Wind @ P" + index + ": time : " + windDTO.timepoint + " speed: " + windDTO.trueWindSpeedInKnots + "knots "
+ windDTO.trueWindBearingDeg;
logger.fine(msg);
final Context2d context2d = canvas.getContext2d();
context2d.setGlobalAlpha(0.2);
final PositionDTO position = windDTO.position;
final LatLng positionLatLng = LatLng.newInstance(position.latDeg, position.lngDeg);
final Point canvasPositionInPx = mapProjection.fromLatLngToDivPixel(positionLatLng);
final double x = canvasPositionInPx.getX() - getWidgetPosLeft();
final double y = canvasPositionInPx.getY() - getWidgetPosTop();
windFieldPoints.put(new ToolTip(x, y), windDTO);
final double dx = length * Math.sin(angle);
final double dy = -length * Math.cos(angle);
final double x1 = x + dx / 2;
final double y1 = y + dy / 2;
drawLine(x - dx / 2, y - dy / 2, x1, y1, weight, arrowColor);
final double theta = Math.atan2(-dy, dx);
final double hLength = Math.max(6.,6.+(10./(60.-10.))*Math.max(length-6.,0));
logger.finer("headlength: "+hLength+", arrowlength: "+length);
if (drawHead) {
drawHead(x1, y1, theta, hLength, weight);
}
//String text = "P" + index;// + NumberFormat.getFormat("0.00").format(windDTO.trueWindBearingDeg) + "�";
//drawPointWithText(x, y, text);
//drawPoint(x, y);
context2d.setGlobalAlpha(0.4);
}
protected void drawHead(final double x, final double y, final double theta, final double headLength, final double weight) {
double t = theta + (Math.PI / 4);
if (t > Math.PI) {
t -= 2 * Math.PI;
}
double t2 = theta - (Math.PI / 4);
if (t2 <= (-Math.PI)) {
t2 += 2 * Math.PI;
}
final double x1 = (x - Math.cos(t) * headLength);
final double y1 = (y + Math.sin(t) * headLength);
final double x1o = (x + Math.cos(t) * weight/2);
final double y1o = (y - Math.sin(t) * weight/2);
final double x2 = (x - Math.cos(t2) * headLength);
final double y2 = (y + Math.sin(t2) * headLength);
final double x2o = (x + Math.cos(t2) * weight/2);
final double y2o = (y - Math.sin(t2) * weight/2);
drawLine(x1o, y1o, x1, y1, weight, arrowHeadColor);
drawLine(x2o, y2o, x2, y2, weight, arrowHeadColor);
this.stopStreamlets();
}
@Override
public void timeChanged(final Date newTime, Date oldTime) {
List<SimulatorWindDTO> windDTOToDraw = new ArrayList<SimulatorWindDTO>();
final SortedMap<Long, List<SimulatorWindDTO>> headMap = (timePointWindDTOMap.headMap(newTime.getTime()+1));
if (!headMap.isEmpty()) {
windDTOToDraw = headMap.get(headMap.lastKey());
}
logger.info("In WindFieldCanvasOverlay.drawWindField drawing " + windDTOToDraw.size() + " points" + " @ "
+ newTime);
drawWindField(windDTOToDraw);
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 (!this.isVisible() || timePointWindDTOMap == null || timer == null || timePointWindDTOMap.isEmpty()) {
return true;
}
if (timePointWindDTOMap.lastKey() < timer.getTime().getTime()) {
return true;
} else {
return false;
}
if (timer.getTime().getTime() >= this.windParams.getEndTime().getTime()) {
return true;
} else {
return false;
}
}
public void setTimer(final Timer timer) {
@@ -50,7 +50,7 @@ public abstract class FullCanvasOverlay extends CanvasOverlayV3 implements Requi
@Override
public void onEvent(CenterChangeMapEvent event) {
// TODO Auto-generated method stub
draw();
drawCenterChanged();
};
});
@@ -86,6 +86,10 @@ public abstract class FullCanvasOverlay extends CanvasOverlayV3 implements Requi
});
}
protected void drawCenterChanged() {
draw();
}
@Override
protected void draw() {
if (mapProjection != null) {
@@ -1,51 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.4.0//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.4.0/distro-source/core/src/gwt-module.dtd">
<module>
<stylesheet src="../reset.css" />
<!-- Inherit the core Web Toolkit stuff. -->
<inherits name='com.google.gwt.user.User'/>
<!-- Inherit the default GWT style sheet. You can change -->
<!-- the theme of your GWT application by uncommenting -->
<!-- any one of the following lines. -->
<!-- inherits name='com.google.gwt.user.theme.clean.Clean'/> -->
<!-- <inherits name='com.google.gwt.user.theme.standard.Standard'/> -->
<!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
<!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/> -->
<stylesheet src="../Simulator.css" />
<!-- Google Maps API -->
<inherits name='com.google.gwt.maps.Maps' />
<!-- Highcharts API -->
<inherits name="org.moxieapps.gwt.highcharts.Highcharts" />
<!-- Other module inherits -->
<inherits name="com.sap.sailing.gwt.ui.EmulatedJRE" />
<inherits name="com.sap.sse.gwt.SSESharedGWT" />
<inherits name="com.sap.sse.SSECommon" />
<inherits name="com.sap.sailing.domain.SailingDomain" />
<inherits name="com.sap.sailing.simulator.Simulator" />
<!-- Specify the app entry point class. -->
<entry-point class='com.sap.sailing.gwt.ui.simulator.SimulatorEntryPoint'/>
<!-- Specify the paths for translatable code -->
<extend-property name="locale" values="de"/>
<source path='shared'/>
<source path='client'/>
<source path='actions'/>
<source path='simulator'/>
<inherits name='com.google.gwt.logging.Logging'/>
<set-property name="gwt.logging.enabled" value="FALSE"/>
<!-- Set logging level to INFO -->
<set-property name="gwt.logging.logLevel" value="INFO"/>
<set-property name="gwt.logging.simpleRemoteHandler" value="ENABLED" />
<set-property name="gwt.logging.consoleHandler" value="DISABLED" />
<set-property name="gwt.logging.popupHandler" value="DISABLED" />
<!-- Add compiler.stackMode to get a readable stacktrace from JavaScript
It generates a set of files in WEB-INF/deploy; those files need to
be placed on the server
-->
<set-property name="compiler.stackMode" value="emulated" />
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.4.0//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.4.0/distro-source/core/src/gwt-module.dtd">
<module>
<stylesheet src="../reset.css" />
<!-- Inherit the core Web Toolkit stuff. -->
<inherits name='com.google.gwt.user.User'/>
<!-- Inherit the default GWT style sheet. You can change -->
<!-- the theme of your GWT application by uncommenting -->
<!-- any one of the following lines. -->
<!-- inherits name='com.google.gwt.user.theme.clean.Clean'/> -->
<!-- <inherits name='com.google.gwt.user.theme.standard.Standard'/> -->
<!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
<!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/> -->
<stylesheet src="../Simulator.css" />
<!-- Google Maps API -->
<inherits name='com.google.gwt.maps.Maps' />
<!-- Highcharts API -->
<inherits name="org.moxieapps.gwt.highcharts.Highcharts" />
<!-- Other module inherits -->
<inherits name="com.sap.sailing.gwt.ui.EmulatedJRE" />
<inherits name="com.sap.sse.gwt.SSESharedGWT" />
<inherits name="com.sap.sse.SSECommon" />
<inherits name="com.sap.sailing.domain.SailingDomain" />
<inherits name="com.sap.sailing.simulator.Simulator" />
<!-- Specify the app entry point class. -->
<entry-point class='com.sap.sailing.gwt.ui.simulator.SimulatorEntryPoint'/>
<!-- Specify the paths for translatable code -->
<extend-property name="locale" values="de"/>
<source path='shared'/>
<source path='client'/>
<source path='actions'/>
<source path='simulator'/>
<script src="../wind-streamlets.js" />
<inherits name='com.google.gwt.logging.Logging'/>
<set-property name="gwt.logging.enabled" value="FALSE"/>
<!-- Set logging level to INFO -->
<set-property name="gwt.logging.logLevel" value="INFO"/>
<set-property name="gwt.logging.simpleRemoteHandler" value="ENABLED" />
<set-property name="gwt.logging.consoleHandler" value="DISABLED" />
<set-property name="gwt.logging.popupHandler" value="DISABLED" />
<!-- Add compiler.stackMode to get a readable stacktrace from JavaScript
It generates a set of files in WEB-INF/deploy; those files need to
be placed on the server
-->
<set-property name="compiler.stackMode" value="emulated" />
</module>
@@ -0,0 +1,820 @@
/**
* 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 latrad = lat*Math.PI/180.0;
var q = Math.log((1.0+Math.sin(latrad))/Math.cos(latrad));
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.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 };
};
VectorField.prototype.getRandomPosition = function() {
var rndY = Math.random();
var rndX = Math.random() - 0.5;
var latP = rndY * this.rcStart.lat + (1.0 - rndY) * this.rcEnd.lat + rndX * this.gvX.lat;
var lngP = rndY * this.rcStart.lng + (1.0 - rndY) * this.rcEnd.lng + rndX * this.gvX.lng;
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) && (idx.y >= 0) && (idx.y < this.resY);
return inBool;
};
VectorField.prototype.interpolate = function(lat, lng) {
var idx = this.getNeighbors(lat, lng);
var idxOff = 0;
if ((idx.xTop >= this.resX)||(idx.yTop >= this.resY)) {
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);
var idxOff = 0;
if ((idx.xTop >= this.resX)||(idx.yTop >= this.resY)) {
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 );
var xIdx = Math.round( posR.lat * this.nvX.lat + posR.lng * this.nvX.lng + (this.resX - 1) / 2. );
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;
var xFlt = posR.lat * this.nvX.lat + posR.lng * this.nvX.lng + (this.resX - 1) / 2.;
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) {
xTop = this.resX-1;
}
if (yTop >= this.resY) {
yTop = this.resY-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.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.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.colors = [];
this.rgb = '40, 40, 40';
this.background = 'rgb(' + this.rgb + ')';
var greyValue = 255;
for (var i = 0; i < 256; i++) {
this.colors[i] = 'rgba(' + (greyValue) + ',' + (greyValue) + ',' + (greyValue) + ',' + (i/255) + ')';
//this.colors[i] = 'rgba(' + (255) + ',' + (255) + ',' + (255) + ',' + 1.0 + ')';
//this.colors[i] = 'hsla(' + 360*(0.55+0.9*(0.5-i/255)) + ',' + (100) + '% ,' + (50) + '%,' + (i/255) + ')';
}
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.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)");
};
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;
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); //, 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;
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);
}