mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-13 15:50:59 +00:00
Merge branch 'origin/simulator_maps_v3'
This commit is contained in:
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/,\
|
||||
|
||||
+93
-20
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -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;
|
||||
|
||||
+9
@@ -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;
|
||||
}
|
||||
|
||||
+12
@@ -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();
|
||||
|
||||
+16
-6
@@ -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()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+9
-6
@@ -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 {
|
||||
|
||||
+44
-109
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+23
-4
@@ -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);
|
||||
|
||||
+14
@@ -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();
|
||||
|
||||
}
|
||||
+72
-31
@@ -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() {
|
||||
|
||||
+207
-28
@@ -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());
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -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;
|
||||
|
||||
+1
-1
@@ -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();
|
||||
|
||||
+273
@@ -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;
|
||||
}
|
||||
}
|
||||
+212
-199
@@ -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) {
|
||||
|
||||
+5
-1
@@ -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) {
|
||||
|
||||
+118748
File diff suppressed because it is too large.
Load diff
Executable → Regular
+52
-50
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user