diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java index 9a05c5becb9..d61e6d00696 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java @@ -38,9 +38,9 @@ import com.sap.sailing.gwt.ui.shared.RequestPolarDiagramDataDTO; import com.sap.sailing.gwt.ui.shared.SimulatedPathsEvenTimedResultDTO; import com.sap.sailing.gwt.ui.shared.SimulatorResultsDTO; import com.sap.sailing.gwt.ui.shared.SimulatorResultsDTOAndNotificationMessage; +import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; import com.sap.sailing.gwt.ui.shared.SpeedBearingPositionDTO; import com.sap.sailing.gwt.ui.shared.SpeedWithBearingDTO; -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.shared.WindLatticeDTO; @@ -327,14 +327,14 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula windFieldDTO.setMatrix(wList); if (isShowLines) { - + if (seedLines == 'f') { getWindLinesFromStartLine(wf, windFieldDTO, startTime, endTime, timeStep); } if (seedLines == 'b') { getWindLinesFromEndLine(wf, windFieldDTO, startTime, endTime, timeStep); } - + } return windFieldDTO; } @@ -352,9 +352,9 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula for (int j = 1; j < positionGrid[0].length - 1; ++j) { // for (int j = 0; j < positionGrid[0].length; ++j) { TimePoint t = startTime; - Position p0 = positionGrid[0][j]; - Position p1 = positionGrid[1][j]; - Position seed = new DegreePosition(p0.getLatDeg() + 0.5*(p0.getLatDeg()-p1.getLatDeg()), p0.getLngDeg() + 0.5*(p0.getLngDeg()-p1.getLngDeg())); + final Position p0 = positionGrid[0][j]; + final Position p1 = positionGrid[1][j]; + final Position seed = new DegreePosition(p0.getLatDeg() + 0.5*(p0.getLatDeg()-p1.getLatDeg()), p0.getLngDeg() + 0.5*(p0.getLngDeg()-p1.getLngDeg())); final PositionDTO startPosition = new PositionDTO(seed.getLatDeg(), seed.getLngDeg()); while (t.compareTo(endTime) <= 0) { final TimedPosition tp = new TimedPositionImpl(t, seed); @@ -392,9 +392,9 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula for (int j = 1; j < positionGrid[lastRowIndex].length - 1; ++j) { TimePoint t = startTime; - Position p0 = positionGrid[lastRowIndex][j]; - Position p1 = positionGrid[lastRowIndex-1][j]; - Position seed = new DegreePosition(p0.getLatDeg() + 0.5*(p0.getLatDeg()-p1.getLatDeg()), p0.getLngDeg() + 0.5*(p0.getLngDeg()-p1.getLngDeg())); + final Position p0 = positionGrid[lastRowIndex][j]; + final Position p1 = positionGrid[lastRowIndex-1][j]; + final Position seed = new DegreePosition(p0.getLatDeg() + 0.5*(p0.getLatDeg()-p1.getLatDeg()), p0.getLngDeg() + 0.5*(p0.getLngDeg()-p1.getLngDeg())); final PositionDTO startPosition = new PositionDTO(seed.getLatDeg(), seed.getLngDeg()); while (t.compareTo(endTime) <= 0) { final TimedPosition tp = new TimedPositionImpl(t, seed); @@ -466,7 +466,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula final List path = paths.get(pathName); // TODO: cleanup getAllPathsEvenTimed to return Paths instead of PathPoints - if (pathName.equals("6#GPS Poly")) { + if (pathName.equals("6#GPS Poly") || pathName.equals("7#GPS Track")) { // special initialization for polyline pathDTO[pathIndex] = this.convertToPathDTOAndMarkTurns(new PathImpl(paths.get(pathName), null), pathName.split("#")[1]); @@ -784,7 +784,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula speeds.add(new SpeedBearingPositionDTO(positionDTO, new SpeedWithBearingDTO(speedWithBearing.getKnots(), - speedWithBearing.getBearing().getDegrees()))); + speedWithBearing.getBearing().getDegrees()))); } return new ReceivePolarDiagramDataDTO(speeds, polarDiagramAndNotificationMessage.getNotificationMesssage()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/SimulatorWindDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/SimulatorWindDTO.java index c15f7ee5fc0..db3e3ab0ced 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/SimulatorWindDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/SimulatorWindDTO.java @@ -4,19 +4,49 @@ import com.google.gwt.user.client.rpc.IsSerializable; public class SimulatorWindDTO implements IsSerializable { public boolean isTurn; - //public Double trueWindSpeedInMetersPerSecond; public Double trueWindSpeedInKnots; public Double trueWindBearingDeg; - //public Double trueWindFromDeg; - //public Double dampenedTrueWindSpeedInMetersPerSecond; - //public Double dampenedTrueWindSpeedInKnots; - //public Double dampenedTrueWindBearingDeg; - //public Double dampenedTrueWindFromDeg; public PositionDTO position; public Long timepoint; - //public Long originTimepoint; - //public Double confidence; public SimulatorWindDTO() { } + + @Override + public int hashCode() { + + final int prime = 31; + int result = 1; + + long temp = 0; + + temp = this.trueWindSpeedInKnots.intValue(); + result = prime * result + (int) (temp ^ (temp >>> 32)); + + temp = this.trueWindBearingDeg.intValue(); + result = prime * result + (int) (temp ^ (temp >>> 32)); + + temp = this.timepoint.intValue(); + result = prime * result + (int) (temp ^ (temp >>> 32)); + + temp = this.position.hashCode(); + result = prime * result + (int) (temp ^ (temp >>> 32)); + + return result; + + } + + @Override + public boolean equals(final Object o) { + if (o == null) { + return false; + } else { + if (o instanceof SimulatorWindDTO) { + final SimulatorWindDTO other = (SimulatorWindDTO) o; + + return this.position.equals(other.position) && (Math.abs(this.timepoint - other.timepoint) < 9999); + } + return false; + } + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathCanvasOverlay.java index 099abb365ef..b2b59756692 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathCanvasOverlay.java @@ -3,7 +3,6 @@ package com.sap.sailing.gwt.ui.simulator; import java.util.Date; import java.util.Iterator; import java.util.List; -import java.util.logging.Logger; import com.google.gwt.canvas.dom.client.Context2d; import com.google.gwt.i18n.client.DateTimeFormat; @@ -33,13 +32,15 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named { */ private static final long serialVersionUID = -6284996043723173190L; - private static Logger logger = Logger.getLogger(PathCanvasOverlay.class.getName()); - + // private static Logger logger = Logger.getLogger(PathCanvasOverlay.class.getName()); + private static final int MinimumPxDistanceBetweenArrows = 60; - + private LatLng startPoint; private LatLng endPoint; - + private boolean totalTimeIsGiven = false; + private long totalTimeMilliseconds = 0; + protected String name; public String pathColor = "Green"; @@ -48,21 +49,44 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named { */ public boolean displayWindAlongPath = true; - public PathCanvasOverlay(String name) { + public PathCanvasOverlay(final String name) { super(); this.name = name; } - - public PathCanvasOverlay(String name, Timer timer) { + + public PathCanvasOverlay(final String name, final Timer timer) { super(timer); this.name = name; } - - public void setRaceCourse(LatLng startPoint, LatLng endPoint) { - this.startPoint = startPoint; - this.endPoint = endPoint; + + public PathCanvasOverlay(final String name, final long totalTimeMilliseconds) { + super(); + this.name = name; + this.totalTimeIsGiven = true; + this.totalTimeMilliseconds = totalTimeMilliseconds; } - + + public PathCanvasOverlay(final String name, final Timer timer, final long totalTimeMilliseconds) { + super(timer); + this.name = name; + this.totalTimeIsGiven = true; + this.totalTimeMilliseconds = totalTimeMilliseconds; + } + + public void setTotalTimeMilliseconds(final long totalTimeMilliseconds) { + this.totalTimeIsGiven = true; + this.totalTimeMilliseconds = totalTimeMilliseconds; + } + + public long getTotalTimeMilliseconds() { + return this.totalTimeMilliseconds; + } + + public void setRaceCourse(final LatLng startPoint, final LatLng endPoint) { + this.startPoint = startPoint; + this.endPoint = endPoint; + } + /* @Override protected void drawWindField() { @@ -71,41 +95,43 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named { drawWindField(windDTOList); } */ + @Override protected void drawWindField(final List windDTOList) { - WindFieldGenParamsDTO windParams = new WindFieldGenParamsDTO(); + final WindFieldGenParamsDTO windParams = new WindFieldGenParamsDTO(); - int numPoints = windDTOList.size(); + final int numPoints = windDTOList.size(); if (numPoints < 1) { return; } String title = "Path at " + numPoints + " points."; - long totalTime = windDTOList.get(numPoints - 1).timepoint - windDTOList.get(0).timepoint; + final long totalTime = windDTOList.get(numPoints - 1).timepoint - windDTOList.get(0).timepoint; //LatLng start = LatLng.newInstance(windDTOList.get(0).position.latDeg, windDTOList.get(0).position.latDeg); //LatLng end = LatLng.newInstance(windDTOList.get(numPoints - 1).position.latDeg, windDTOList.get(numPoints - 1).position.latDeg); - double distance = startPoint.distanceFrom(endPoint) / Mile.METERS_PER_NAUTICAL_MILE; + final double distance = startPoint.distanceFrom(endPoint) / Mile.METERS_PER_NAUTICAL_MILE; - Point startPx = getMap().convertLatLngToDivPixel(startPoint); - Point endPx = getMap().convertLatLngToDivPixel(endPoint); - double rcLengthPx = Math.sqrt(Math.pow(startPx.getX() - endPx.getX(), 2) + Math.pow(startPx.getY() - endPx.getY(), 2)); - //System.out.println("Race Course Pixel Length: "+rcLengthPx); + // final Point startPx = getMap().convertLatLngToDivPixel(startPoint); + // final Point endPx = getMap().convertLatLngToDivPixel(endPoint); + // final double rcLengthPx = Math.sqrt(Math.pow(startPx.getX() - endPx.getX(), 2) + Math.pow(startPx.getY() - + // endPx.getY(), 2)); + // System.out.println("Race Course Pixel Length: "+rcLengthPx); - double arrowDistPx = 60; - long arrowInterleave = Math.max(1, Math.round(arrowDistPx * numPoints / rcLengthPx)); + // final double arrowDistPx = 60; + // final long arrowInterleave = Math.max(1, Math.round(arrowDistPx * numPoints / rcLengthPx)); // System.out.print("Arrow Interleave: "+arrowInterleave+"\n"); - //arrowInterleave = 3; - + // arrowInterleave = 3; + if (windDTOList != null && windDTOList.size() > 0) { - Context2d context2d = canvas.getContext2d(); + final Context2d context2d = canvas.getContext2d(); context2d.setGlobalAlpha(0.8); Iterator windDTOIter = windDTOList.iterator(); SimulatorWindDTO prevWindDTO = null; while (windDTOIter.hasNext()) { - SimulatorWindDTO windDTO = windDTOIter.next(); + final SimulatorWindDTO windDTO = windDTOIter.next(); if (prevWindDTO != null) { drawLine(prevWindDTO, windDTO); } @@ -114,24 +140,24 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named { windDTOIter = windDTOList.iterator(); int index = 0; - long startTime = windDTOList.get(0).timepoint; + final long startTime = windDTOList.get(0).timepoint; prevWindDTO = null; //For the last time arrow was displayed while (windDTOIter.hasNext()) { - SimulatorWindDTO windDTO = windDTOIter.next(); + final SimulatorWindDTO windDTO = windDTOIter.next(); //if ((displayWindAlongPath) && ((index % arrowInterleave) == 0)) { if (displayWindAlongPath) { if (checkPointsAreFarEnough(windDTO,prevWindDTO)) { - DegreeBearingImpl dbi = new DegreeBearingImpl(windDTO.trueWindBearingDeg); - // System.out.print("index: "+index+"\n"); - + final DegreeBearingImpl dbi = new DegreeBearingImpl(windDTO.trueWindBearingDeg); + // System.out.print("index: "+index+"\n"); + drawScaledArrow(windDTO, dbi.getRadians(), index, true); prevWindDTO = windDTO; } } index++; - long timeStep = windParams.getTimeStep().getTime(); + final long timeStep = windParams.getTimeStep().getTime(); if ((windDTO.timepoint - startTime) % (timeStep) == 0) { drawPoint(windDTO); } @@ -141,8 +167,8 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named { context2d.setGlobalAlpha(1.0); // MeterDistance meterDistance = new MeterDistance(distance); - Date timeDiffDate = new Date(totalTime); - TimeZone gmt = TimeZone.createTimeZone(0); + final Date timeDiffDate = new Date(totalTime); + final TimeZone gmt = TimeZone.createTimeZone(0); title += " " + NumberFormat.getFormat("0.00").format(distance) + " nmi"; title += " in " + DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.HOUR24_MINUTE_SECOND).format(timeDiffDate, gmt); @@ -151,23 +177,23 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named { } } - private void drawLine(SimulatorWindDTO p1, SimulatorWindDTO p2) { + private void drawLine(final SimulatorWindDTO p1, final SimulatorWindDTO p2) { - double weight = 3.0; + final double weight = 3.0; PositionDTO position = p1.position; LatLng positionLatLng = LatLng.newInstance(position.latDeg, position.lngDeg); Point canvasPositionInPx = getMap().convertLatLngToDivPixel(positionLatLng); - int x1 = canvasPositionInPx.getX() - this.getWidgetPosLeft(); - int y1 = canvasPositionInPx.getY() - this.getWidgetPosTop(); + final int x1 = canvasPositionInPx.getX() - this.getWidgetPosLeft(); + final int y1 = canvasPositionInPx.getY() - this.getWidgetPosTop(); position = p2.position; positionLatLng = LatLng.newInstance(position.latDeg, position.lngDeg); canvasPositionInPx = getMap().convertLatLngToDivPixel(positionLatLng); - int x2 = canvasPositionInPx.getX() - this.getWidgetPosLeft(); - int y2 = canvasPositionInPx.getY() - this.getWidgetPosTop(); + final int x2 = canvasPositionInPx.getX() - this.getWidgetPosLeft(); + final int y2 = canvasPositionInPx.getY() - this.getWidgetPosTop(); // Context2d context2d = canvas.getContext2d(); // context2d.setShadowBlur(weight); @@ -176,17 +202,17 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named { // System.out.print("x1:"+x1+" y1:"+y1+" x2:"+x2+" y2:"+y2+"\n"); } - private void drawPoint(SimulatorWindDTO p) { + private void drawPoint(final SimulatorWindDTO p) { - double weight = 3.0; + final double weight = 3.0; - PositionDTO position = p.position; + final PositionDTO position = p.position; - LatLng positionLatLng = LatLng.newInstance(position.latDeg, position.lngDeg); - Point canvasPositionInPx = getMap().convertLatLngToDivPixel(positionLatLng); + final LatLng positionLatLng = LatLng.newInstance(position.latDeg, position.lngDeg); + final Point canvasPositionInPx = getMap().convertLatLngToDivPixel(positionLatLng); - int x1 = canvasPositionInPx.getX() - this.getWidgetPosLeft(); - int y1 = canvasPositionInPx.getY() - this.getWidgetPosTop(); + final int x1 = canvasPositionInPx.getX() - this.getWidgetPosLeft(); + final int y1 = canvasPositionInPx.getY() - this.getWidgetPosTop(); drawCircle(x1, y1, weight / 2., pathColor); } @@ -196,7 +222,7 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named { * @return true if the pixel distance between the two points is greater than * a threshold. returns true if either of the points is null */ - private boolean checkPointsAreFarEnough(SimulatorWindDTO p1, SimulatorWindDTO p2) { + private boolean checkPointsAreFarEnough(final SimulatorWindDTO p1, final SimulatorWindDTO p2) { if (p1 == null || p2 == null) { return true; } @@ -205,32 +231,36 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named { LatLng positionLatLng = LatLng.newInstance(position.latDeg, position.lngDeg); Point canvasPositionInPx = getMap().convertLatLngToDivPixel(positionLatLng); - int x1 = canvasPositionInPx.getX(); - int y1 = canvasPositionInPx.getY(); + final int x1 = canvasPositionInPx.getX(); + final int y1 = canvasPositionInPx.getY(); position = p2.position; positionLatLng = LatLng.newInstance(position.latDeg, position.lngDeg); canvasPositionInPx = getMap().convertLatLngToDivPixel(positionLatLng); - int x2 = canvasPositionInPx.getX(); - int y2 = canvasPositionInPx.getY(); + final int x2 = canvasPositionInPx.getX(); + final int y2 = canvasPositionInPx.getY(); - double pxDistance = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); + final double pxDistance = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); if (pxDistance >= MinimumPxDistanceBetweenArrows) { return true; } else { - return false; + return false; } } - + @Override public String getName() { return name; } public long getPathTime() { - List windDTOList = wl.getMatrix(); - int numPoints = windDTOList.size(); - long totalTime = windDTOList.get(numPoints - 1).timepoint - windDTOList.get(0).timepoint; - return totalTime; + if (this.totalTimeIsGiven) { + return this.totalTimeMilliseconds; + } else { + final List windDTOList = wl.getMatrix(); + final int numPoints = windDTOList.size(); + final long totalTime = windDTOList.get(numPoints - 1).timepoint - windDTOList.get(0).timepoint; + return totalTime; + } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java index 08d16b271e6..d4fda336eaa 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java @@ -1,5 +1,6 @@ package com.sap.sailing.gwt.ui.simulator; +import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -38,7 +39,7 @@ public class PathLegendCanvasOverlay extends CanvasOverlay { public String textColor = "Black"; public String textFont = "normal 10pt UbuntuRegular"; - + public PathLegendCanvasOverlay() { this.setPathOverlays(null); } @@ -49,46 +50,46 @@ public class PathLegendCanvasOverlay extends CanvasOverlay { } @Override - protected void redraw(boolean force) { - + protected void redraw(final boolean force) { + if (pathOverlays == null || pathOverlays.size() < 1) { return; } setCanvasSettings(); int index = 0; - Context2d context2d = canvas.getContext2d(); + final Context2d context2d = canvas.getContext2d(); context2d.setFont(textFont); TextMetrics txtmet; txtmet = context2d.measureText("00:00:00"); - double timewidth = txtmet.getWidth(); + final double timewidth = txtmet.getWidth(); double txtmaxwidth = 0.0; - for (PathCanvasOverlay path : pathOverlays) { - txtmet = context2d.measureText(path.name); - txtmaxwidth = Math.max(txtmaxwidth, txtmet.getWidth()); + for (final PathCanvasOverlay path : pathOverlays) { + txtmet = context2d.measureText(path.name); + txtmaxwidth = Math.max(txtmaxwidth, txtmet.getWidth()); } - for (PathCanvasOverlay path : pathOverlays) { - drawRectangleWithText(xOffset, yOffset + (pathOverlays.size()-1-index) * rectHeight, path.pathColor, + for (final PathCanvasOverlay path : pathOverlays) { + drawRectangleWithText(xOffset, yOffset + (pathOverlays.size()-1-index) * rectHeight, path.pathColor, path.name, getFormattedTime(path.getPathTime()),txtmaxwidth,timewidth); index++; } } protected void setCanvasSettings() { - int canvasWidth = (int) rectWidth + 200; - int canvasHeight = getMap().getSize().getHeight(); + final int canvasWidth = (int) rectWidth + 200; + final int canvasHeight = getMap().getSize().getHeight(); canvas.setWidth(String.valueOf(canvasWidth)); canvas.setHeight(String.valueOf(canvasHeight)); canvas.setCoordinateSpaceWidth(canvasWidth); canvas.setCoordinateSpaceHeight(canvasHeight); - Point sw = getMap().convertLatLngToDivPixel(getMap().getBounds().getSouthWest()); - Point ne = getMap().convertLatLngToDivPixel(getMap().getBounds().getNorthEast()); + final Point sw = getMap().convertLatLngToDivPixel(getMap().getBounds().getSouthWest()); + final Point ne = getMap().convertLatLngToDivPixel(getMap().getBounds().getNorthEast()); setWidgetPosLeft(Math.min(sw.getX(), ne.getX())); setWidgetPosTop(Math.min(sw.getY(), ne.getY())); //setWidgetPosLeft(xOffset); //setWidgetPosTop(yOffset); - + getPane().setWidgetPosition(getCanvas(), getWidgetPosLeft(), getWidgetPosTop()); } @@ -97,15 +98,39 @@ public class PathLegendCanvasOverlay extends CanvasOverlay { return pathOverlays; } - public void setPathOverlays(List pathOverlays) { + public void setPathOverlays(final List pathOverlays) { + this.pathOverlays = pathOverlays; } + public void addPathOverlay(final PathCanvasOverlay pathOverlay) { + + if (this.pathOverlays == null) { + this.pathOverlays = new ArrayList(); + } + + boolean found = false; + + if (pathOverlay.name.equals("Polyline")) { + for (final PathCanvasOverlay overlay : this.pathOverlays) { + if (overlay.name.equals("Polyline")) { + overlay.setTotalTimeMilliseconds(pathOverlay.getTotalTimeMilliseconds()); + found = true; + break; + } + } + } + + if (found == false) { + this.pathOverlays.add(pathOverlay); + } + } + public int getWidgetPosLeft() { return widgetPosLeft; } - public void setWidgetPosLeft(int widgetPosLeft) { + public void setWidgetPosLeft(final int widgetPosLeft) { this.widgetPosLeft = widgetPosLeft; } @@ -113,23 +138,23 @@ public class PathLegendCanvasOverlay extends CanvasOverlay { return widgetPosTop; } - public void setWidgetPosTop(int widgetPosTop) { + public void setWidgetPosTop(final int widgetPosTop) { this.widgetPosTop = widgetPosTop; } - protected void drawRectangle(double x, double y, String color) { - Context2d context2d = canvas.getContext2d(); + protected void drawRectangle(final double x, final double y, final String color) { + final Context2d context2d = canvas.getContext2d(); context2d.setFillStyle(color); context2d.setLineWidth(3); context2d.fillRect(x, y, rectWidth, rectHeight); } - protected void drawRectangleWithText(double x, double y, String color, String text, String time, double textmaxwidth, double timewidth) { + protected void drawRectangleWithText(final double x, final double y, final String color, final String text, final String time, final double textmaxwidth, final double timewidth) { - double offset = 3.0; - - Context2d context2d = canvas.getContext2d(); + final double offset = 3.0; + + final Context2d context2d = canvas.getContext2d(); context2d.setFont(textFont); drawRectangle(x, y, color); context2d.setGlobalAlpha(0.80); @@ -141,10 +166,10 @@ public class PathLegendCanvasOverlay extends CanvasOverlay { context2d.fillText(time, x + rectWidth + textmaxwidth + 10.0, y + 12.0 + offset); } - protected String getFormattedTime(long pathTime) { - TimeZone gmt = TimeZone.createTimeZone(0); - Date timeDiffDate = new Date(pathTime); - String pathTimeStr = DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.HOUR24_MINUTE_SECOND).format( + protected String getFormattedTime(final long pathTime) { + final TimeZone gmt = TimeZone.createTimeZone(0); + final Date timeDiffDate = new Date(pathTime); + final String pathTimeStr = DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.HOUR24_MINUTE_SECOND).format( timeDiffDate, gmt); return pathTimeStr; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathPolyline.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathPolyline.java index 41f5ce80def..e7271d64d12 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathPolyline.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathPolyline.java @@ -16,7 +16,6 @@ import com.google.gwt.maps.client.overlay.Polyline; import com.google.gwt.user.client.rpc.AsyncCallback; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.SimulatorServiceAsync; -import com.sap.sailing.gwt.ui.shared.PathDTO; import com.sap.sailing.gwt.ui.shared.PositionDTO; import com.sap.sailing.gwt.ui.shared.ReceivePolarDiagramDataDTO; import com.sap.sailing.gwt.ui.shared.RequestPolarDiagramDataDTO; @@ -33,7 +32,7 @@ public class PathPolyline { private static final boolean SHOULD_DRAW_VERTICAL_DASHLINES = true; private static final boolean SHOULD_DRAW_SHADOW_DASHLINES = false; - private static final String DEFAULT_COLOR = "#8B0000"; + public static final String DEFAULT_COLOR = "#8B0000"; private static final int DEFAULT_WEIGHT = 3; private static final double DEFAULT_OPACITY = 1.0; private static final double DEFAULT_DISTANCE_PX = 25; @@ -61,7 +60,7 @@ public class PathPolyline { private SpeedWithBearingDTO averageWindSpeed; private final int boatClassID; - private final PathDTO pathDTO; + private final List pathPoints; private final SimulatorServiceAsync simulatorService; private final MapWidget map; private final ErrorReporter errorReporter; @@ -69,17 +68,20 @@ public class PathPolyline { private boolean firstTime = true; + private SimulatorMap simulatorMap = null; + // private double stepSizeMeters = 5.0; // private static Logger logger = Logger.getLogger(PathPolyline.class.getName()); - public PathPolyline(final LatLng[] points, final int boatClassID, final ErrorReporter errorReporter, final PathDTO pathDTO, - final SimulatorServiceAsync simulatorService, final MapWidget map) { - this(points, DEFAULT_COLOR, DEFAULT_WEIGHT, DEFAULT_OPACITY, boatClassID, errorReporter, pathDTO, simulatorService, map); + public PathPolyline(final LatLng[] points, final int boatClassID, final ErrorReporter errorReporter, final List pathPoints, + final SimulatorServiceAsync simulatorService, final MapWidget map, final SimulatorMap simulatorMap) { + this(points, DEFAULT_COLOR, DEFAULT_WEIGHT, DEFAULT_OPACITY, boatClassID, errorReporter, pathPoints, simulatorService, map, simulatorMap); } public PathPolyline(final LatLng[] points, final String color, final int weight, final double opacity, final int boatClassID, - final ErrorReporter errorReporter, final PathDTO pathDTO, final SimulatorServiceAsync simulatorService, final MapWidget map) { + final ErrorReporter errorReporter, final List pathPoints, final SimulatorServiceAsync simulatorService, final MapWidget map, + final SimulatorMap simulatorMap) { this.points = points; this.color = color; this.weight = weight; @@ -89,13 +91,15 @@ public class PathPolyline { this.originAndHeads = new HashMap>(); this.shadowPoints = new ArrayList(); - this.pathDTO = pathDTO; + this.pathPoints = pathPoints; this.simulatorService = simulatorService; this.map = map; this.averageWindSpeed = new SpeedWithBearingDTO(); this.boatClassID = boatClassID; this.errorReporter = errorReporter; + this.simulatorMap = simulatorMap; + this.drawPolylineOnMap(); this.drawDashLinesOnMap(this.map.getZoomLevel()); @@ -388,19 +392,22 @@ public class PathPolyline { System.err.println("=================================================="); System.err.println("total distance = " + PathPolylineUtils.getTotalDistanceMeters(points) + " meters"); System.err.println("step size = " + STEP_SIZE_METERS + " meters"); - System.err.println("average wind speed = " + PathPolylineUtils.knotsToMetersPerSecond(averageWindSpeed.speedInKnots) + " meters/second"); - System.err.println("total time = " + totalTime + " seconds!"); + System.err.println("average wind speed = " + PathPolylineUtils.knotsToMetersPerSecond(averageWindSpeed.speedInKnots) + + " meters/second, with bearing of " + averageWindSpeed.bearingInDegrees + " degrees"); + System.err.println("total time = " + (long) totalTime + " seconds!"); System.err.println("=================================================="); + + simulatorMap.addLegendOverlayForPathPolyline(((long) totalTime) * 1000); + simulatorMap.redrawLegendCanvasOverlay(); } }); } private SpeedWithBearingDTO getAverageWindSpeed() { - final List windDTOs = this.pathDTO.getMatrix(); // PathPolylineUtils.computeMinMaxAverageWindSpeed(windDTOs) - return PathPolylineUtils.computeAverageWindSpeed(windDTOs); + return PathPolylineUtils.computeAverageWindSpeed(this.pathPoints); } private RequestPolarDiagramDataDTO createRequestPolarDiagramDataDTO() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java index f3dda3e125d..34ed173ecb9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java @@ -1,6 +1,7 @@ package com.sap.sailing.gwt.ui.simulator; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; @@ -68,8 +69,11 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ private final int xRes; private final int yRes; - // private PathPolyline pathPolyline; private boolean warningAlreadyShown = false; + private boolean firstTime = true; + + private PathDTO gpsPoly = null; + private PathDTO gpsTrack = null; public enum ViewName { SUMMARY, REPLAY, WINDDISPLAY @@ -136,11 +140,18 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ pathName = paths[index].name; color = colorPalette.getColor(paths.length - 1 - index); - // TODO: "GPS Poly" has wrong wind information => fix douglas-peucker "approximate", or refer to "GPS Track" - if (pathName.equals("GPS Poly")) { - // pathPolyline = createPathPolyline(currentPath, simulatorSvc, mapw); - createPathPolyline(currentPath, errorReporter, simulatorSvc, mapw); + if (pathName.equals("GPS Track")) { + gpsTrack = currentPath; } + else if (pathName.equals("GPS Poly")) { + gpsPoly = currentPath; + } + + if (firstTime && gpsTrack != null && gpsPoly != null) { + createPathPolyline(identifyPath(gpsTrack, gpsPoly)); + firstTime = false; + } + /* TODO Revisit for now creating a WindFieldDTO from the path */ final WindFieldDTO pathWindDTO = new WindFieldDTO(); @@ -204,7 +215,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ if (windParams.isShowGrid()) { timeListeners.add(windGridCanvasOverlay); } - if (windParams.isShowLines()) { + if (windParams.isShowLines()) { timeListeners.add(windLineCanvasOverlay); } for (int i = 0; i < replayPathCanvasOverlays.size(); ++i) { @@ -687,9 +698,85 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ return value; } - private static PathPolyline createPathPolyline(final PathDTO pathDTO, final ErrorReporter errorReporter, final SimulatorServiceAsync simulatorService, - final MapWidget map) { - final List pathPoints = pathDTO.getMatrix(); + private static List identifyPath(final PathDTO gpsTrack, final PathDTO gpsPoly) { + + final List windDTOs = new ArrayList(); + + final List gpsTrackWindDTOs = gpsTrack.getMatrix(); + final List gpsPolyWindDTOs = gpsPoly.getMatrix(); + + final int countOfGpsPolyWindDTOs = gpsPolyWindDTOs.size(); + if (countOfGpsPolyWindDTOs == 0 || countOfGpsPolyWindDTOs == 1) { + return null; + } + + final SimulatorWindDTO startWindDTO = gpsPolyWindDTOs.get(0); + final SimulatorWindDTO endWindDTO = gpsPolyWindDTOs.get(countOfGpsPolyWindDTOs - 1); + + final int startIndex = getIndexOfClosest(gpsTrackWindDTOs, startWindDTO); + // System.err.println("BBBB: startIndex = " + startIndex); + final int endIndex = getIndexOfClosest(gpsTrackWindDTOs, endWindDTO); + // System.err.println("BBBB: endIndex = " + endIndex); + + for (int index = startIndex; index <= endIndex; index++) { + windDTOs.add(gpsTrackWindDTOs.get(index)); + } + + return windDTOs; + } + + private static int getIndexOfClosest(final List items, final SimulatorWindDTO item) { + final int count = items.size(); + + final List diff_lat = new ArrayList(); + final List diff_lng = new ArrayList(); + final List diff_timepoint = new ArrayList(); + + for (int index = 0; index < count; index++) { + diff_lat.add(Math.abs(items.get(index).position.latDeg - item.position.latDeg)); + diff_lng.add(Math.abs(items.get(index).position.lngDeg - item.position.lngDeg)); + diff_timepoint.add(Math.abs(items.get(index).timepoint - item.timepoint)); + } + + final double min_diff_lat = Collections.min(diff_lat); + final double min_max_diff_lat = min_diff_lat + Collections.max(diff_lat); + + final double min_diff_lng = Collections.min(diff_lng); + final double min_max_diff_lng = min_diff_lng + Collections.max(diff_lng); + + final long min_diff_timepoint = Collections.min(diff_timepoint); + final double min_max_diff_timepoint = min_diff_timepoint + Collections.max(diff_timepoint); + + final List norm_diff_lat = new ArrayList(); + final List norm_diff_lng = new ArrayList(); + final List norm_diff_timepoint = new ArrayList(); + + for (int index = 0; index < count; index++) { + norm_diff_lat.add((diff_lat.get(index) - min_diff_lat) / min_max_diff_lat); + norm_diff_lng.add((diff_lng.get(index) - min_diff_lng) / min_max_diff_lng); + norm_diff_timepoint.add((diff_timepoint.get(index) - min_diff_timepoint) / min_max_diff_timepoint); + } + + final List deltas = new ArrayList(); + + for (int index = 0; index < count; index++) { + deltas.add(Math.sqrt(Math.pow(norm_diff_lat.get(index), 2) + Math.pow(norm_diff_lng.get(index), 2) + Math.pow(norm_diff_timepoint.get(index), 2))); + } + + int result = 0; + double min = deltas.get(0); + + for (int index = 0; index < count; index++) { + if (deltas.get(index) < min) { + result = index; + min = deltas.get(index); + } + } + + return result; + } + + private PathPolyline createPathPolyline(final List pathPoints) { final int noOfPathPoints = pathPoints.size(); SimulatorWindDTO currentPathPoint = null; @@ -704,6 +791,26 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ final int boatClassID = 3; // 49er STG - return new PathPolyline(points.toArray(new LatLng[0]), boatClassID, errorReporter, pathDTO, simulatorService, map); + return new PathPolyline(points.toArray(new LatLng[0]), boatClassID, this.errorReporter, pathPoints, this.simulatorSvc, this.mapw, this); } + + @SuppressWarnings("unused") + private PathPolyline createPathPolyline(final PathDTO pathDTO) { + + return this.createPathPolyline(pathDTO.getMatrix()); + } + + public void addLegendOverlayForPathPolyline(final long totalTimeMilliseconds) { + + final PathCanvasOverlay overlay = new PathCanvasOverlay("Polyline", totalTimeMilliseconds); + overlay.pathColor = PathPolyline.DEFAULT_COLOR; + + this.legendCanvasOverlay.addPathOverlay(overlay); + } + + public void redrawLegendCanvasOverlay() { + this.legendCanvasOverlay.setVisible(true); + this.legendCanvasOverlay.redraw(true); + } + }