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 eae793d7582..7313d104241 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 @@ -3,6 +3,7 @@ package com.sap.sailing.gwt.ui.server; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.logging.Logger; import com.google.gwt.user.server.rpc.RemoteServiceServlet; @@ -257,7 +258,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula TimePoint start = new MillisecondsTimePoint(0); TimePoint timeStep = new MillisecondsTimePoint(30 * 1000); wf.generate(start, null, timeStep); - + /* // TODO Get all the paths that need to be displayed List path = getOppurtunisticPath(course, wf); PathDTO[] pathDTO = new PathDTO[2]; @@ -266,6 +267,9 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula pathDTO[1] = new PathDTO("Path 2"); List path1 = getOptimumPath(course, wf); pathDTO[1].setMatrix(path1); + */ + PathDTO[] pathDTO = getSimulatedPaths(course, wf); + return pathDTO; } @@ -295,26 +299,32 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula } - private List getOppurtunisticPath(List course, WindField wf) { + private PathDTO[] getSimulatedPaths(List course, WindField wf) { PolarDiagram pd = new PolarDiagramImpl(1); SimulationParameters sp = new SimulationParametersImpl(course, pd, wf); SailingSimulator solver = new SailingSimulatorImpl(sp); - Path pth = solver.getOpputunisticPath(); - int i = 0; - List wList = new ArrayList(); - for (TimedPositionWithSpeed p : pth.getPathPoints()) { + Map paths = solver.getAllPaths(); + PathDTO[] pathDTO = new PathDTO[paths.size()]; + int pathIndex = 0; + for(String pathName : paths.keySet()) { + logger.info("Path " + pathName); + Path path = paths.get(pathName); + pathDTO[pathIndex] = new PathDTO(pathName); + List wList = new ArrayList(); + for (TimedPositionWithSpeed p : path.getPathPoints()) { - Wind localWind = wf.getWind(pth.getPositionAtTime(p.getTimePoint())); - logger.finer(localWind.toString()); - WindDTO w = createWindDTO(localWind); - // w.trueWindBearingDeg = 10.0*i; - ++i; - wList.add(w); + Wind localWind = wf.getWind(path.getPositionAtTime(p.getTimePoint())); + logger.finer(localWind.toString()); + WindDTO w = createWindDTO(localWind); + + wList.add(w); + } + pathDTO[pathIndex].setMatrix(wList); + ++pathIndex; } - return wList; - + return pathDTO; } public List getWindPatterns() { @@ -375,19 +385,17 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula TimePoint startTime = new MillisecondsTimePoint(0); TimePoint timeStep = new MillisecondsTimePoint(30 * 1000); wf.generate(startTime, null, timeStep); + + Long longestPathTime = 0L; + PathDTO[] pathDTO = getSimulatedPaths(course, wf); + for (int i = 0; i < pathDTO.length; ++i) { + List path = pathDTO[i].getMatrix(); + int pathLength = path.size(); + long pathTime = pathDTO[i].getMatrix().get(pathLength - 1).timepoint - path.get(0).timepoint; + longestPathTime = Math.max(longestPathTime, pathTime); + } - // TODO Get all the paths that need to be displayed - List path = getOppurtunisticPath(course, wf); - Long pathTime = path.get(path.size() - 1).timepoint - path.get(0).timepoint; - PathDTO[] pathDTO = new PathDTO[2]; - pathDTO[0] = new PathDTO("Path 1"); - pathDTO[0].setMatrix(path); - pathDTO[1] = new PathDTO("Path 2"); - List path1 = getOptimumPath(course, wf); - Long pathTime1 = path1.get(path1.size() - 1).timepoint - path1.get(0).timepoint; - pathDTO[1].setMatrix(path1); - - TimePoint endTime = new MillisecondsTimePoint(startTime.asMillis() + Math.max(pathTime, pathTime1)); + TimePoint endTime = new MillisecondsTimePoint(startTime.asMillis() + longestPathTime); WindFieldDTO windFieldDTO = createWindFieldDTO(wf, startTime, endTime, timeStep); SimulatorResultsDTO simulatorResults = new SimulatorResultsDTO(pathDTO, windFieldDTO); diff --git a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java index ac18a8bd58c..66154c941da 100644 --- a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java +++ b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/SimulatorTest.java @@ -56,7 +56,7 @@ public class SimulatorTest { SailingSimulatorImpl sailingSim = new SailingSimulatorImpl(param); Path path = sailingSim.getOptimumPath(); - logger.info("Path with " + path.getPathPoints().size() + "points"); + //logger.info("Path with " + path.getPathPoints().size() + "points"); assertEquals("Number of path points", 2612, path.getPathPoints().size()); /* * for(TimedPositionWithSpeed p : path.getPathPoints()) { logger.info("Position: " + p.getPosition() + " Wind: " @@ -88,15 +88,15 @@ public class SimulatorTest { SailingSimulator solver = new SailingSimulatorImpl(sp); Path pth = solver.getOptimumPath(); - logger.info("Path with " + pth.getPathPoints().size() + "points"); - for (TimedPositionWithSpeed p : pth.getPathPoints()) { + //logger.info("Path with " + pth.getPathPoints().size() + "points"); + /* for (TimedPositionWithSpeed p : pth.getPathPoints()) { // the null in the Wind output is the timestamp - this Wind is time-invariant! System.out.println("Position: " + p.getPosition() + " Wind: " + wf.getWind(new TimedPositionWithSpeedSimple(p.getPosition()))); System.out.println("Position: " + p.getPosition() + " Wind: " + wf.getWind(pth.getPositionAtTime(p.getTimePoint()))); // Wind wind = wf.getWind(pth.getPositionAtTime(p.getTimePoint())); - } + }*/ assertEquals("Number of path points", 37, pth.getPathPoints().size()); } @@ -132,12 +132,12 @@ public class SimulatorTest { SailingSimulator solver = new SailingSimulatorImpl(sp); Path pth = solver.getOptimumPath(); - logger.info("Path with " + pth.getPathPoints().size() + " points"); - for (TimedPositionWithSpeed p : pth.getPathPoints()) { + //logger.info("Path with " + pth.getPathPoints().size() + " points"); + /* for (TimedPositionWithSpeed p : pth.getPathPoints()) { System.out.println("Position: " + p.getPosition() + " Wind: " + wf.getWind(pth.getPositionAtTime(p.getTimePoint()))); // Wind wind = wf.getWind(pth.getPositionAtTime(p.getTimePoint())); - } + }*/ assertEquals("Number of path points", 37, pth.getPathPoints().size()); } @@ -174,16 +174,38 @@ public class SimulatorTest { SailingSimulator solver = new SailingSimulatorImpl(sp); Path pth = solver.getOptimumPath(); - logger.info("Path with " + pth.getPathPoints().size() + " points"); - for (TimedPositionWithSpeed p : pth.getPathPoints()) { + //logger.info("Path with " + pth.getPathPoints().size() + " points"); + /*for (TimedPositionWithSpeed p : pth.getPathPoints()) { System.out.println("Position: " + p.getPosition() + " Wind: " + wf.getWind(pth.getPositionAtTime(p.getTimePoint()))); // Wind wind = wf.getWind(pth.getPositionAtTime(p.getTimePoint())); - } + }*/ assertEquals("Number of path points", 47, pth.getPathPoints().size()); } + @Test + public void testSailingSimulatorDjikstra() { + + Position start = new DegreePosition(48.401856, -140.001526); + Position end = new DegreePosition(49.143987, -139.987783); + //System.out.println(start.getDistance(end).getKilometers()); + + List course = new LinkedList(); + course.add(start); + course.add(end); + PolarDiagram pd = new PolarDiagramImpl(0); + RectangularBoundary bd = new RectangularBoundary(start, end, 0.1); + KilometersPerHourSpeedImpl kmhrSpeed = new KilometersPerHourSpeedImpl(7.2); + WindControlParameters windParameters = new WindControlParameters(kmhrSpeed.getKnots(), 45); + WindField wf = new WindFieldImpl(bd, windParameters); + SimulationParameters param = new SimulationParametersImpl(course, pd, wf); + + SailingSimulatorImpl sailingSim = new SailingSimulatorImpl(param); + Path path = sailingSim.getAllPaths().get("Djikstra"); + System.out.println(path.getPathPoints().size()); + assertEquals("none", 1, 1); + } } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SailingSimulator.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SailingSimulator.java index e112d644f1d..4873966d545 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SailingSimulator.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/SailingSimulator.java @@ -1,12 +1,13 @@ package com.sap.sailing.simulator; +import java.util.Map; + public interface SailingSimulator { void setSimulationParameters(SimulationParameters params); SimulationParameters getSimulationParameters(); Path getOptimumPath(); - - Path getOpputunisticPath(); + Map getAllPaths(); } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java index de1ed28b476..ac443d82931 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java @@ -1,18 +1,19 @@ package com.sap.sailing.simulator.impl; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; import java.util.List; +import java.util.Map; import com.sap.sailing.domain.base.SpeedWithBearing; -import com.sap.sailing.domain.base.impl.MeterDistance; import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.TimePoint; -import com.sap.sailing.domain.common.impl.DegreeBearingImpl; -import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.simulator.Boundary; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.PolarDiagram; @@ -124,164 +125,136 @@ public class SailingSimulatorImpl implements SailingSimulator { return new PathImpl(lst); } - private Path createForwardDynamic() { + private Path createDjikstra() { + //retrieve simulation parameters Boundary boundary = simulationParameters.getBoundaries(); - WindField wf = simulationParameters.getWindField(); - PolarDiagram pd = simulationParameters.getBoatPolarDiagram(); + WindField windField = simulationParameters.getWindField(); + PolarDiagram polarDiagram = simulationParameters.getBoatPolarDiagram(); Position start = simulationParameters.getCourse().get(0); Position end = simulationParameters.getCourse().get(1); TimePoint startTime = new MillisecondsTimePoint(0); - List lst = new ArrayList(); - - Position currentPosition = start; - TimePoint currentTime = startTime; + //the solution path + LinkedList lst = new LinkedList(); + + //initiate grid int gridv = 10; // number of vertical grid steps int gridh = 30; // number of horizontal grid steps Position[][] sailGrid = boundary.extractGrid(gridh, gridv); - - - - /* - stretch.x <- 0.5 ## stretch-factor for horizontal grid - - grid.x <- (-grid.h:grid.h)/grid.h*stretch.x - grid.y <- (0:grid.v)/grid.v - - sail.grid <- expand.grid(grid.x,grid.y) - */ - - - /*for(idxv=1; idxv<=hridv; idxv++) { - - paths.new <- vector("list",2*grid.h+1) - duras.new <- vector("list",2*grid.h+1) - alldur <- NULL - - if (idx.v==1) { - range1 <- 0 - } else { - range1 <- grid.h - } - - if (idx.v==grid.v) { - range2 <- 0 - } else { - range2 <- grid.h - } - - for(idx.h2 in -range2:range2) { - - alldur <- NULL - - for(idx.h1 in -range1:range1) { - - duration.h1h2 <- duration(idx.h1,idx.h2,idx.v) - - if (duration.h1h2 == Inf) { - next - } - - if (idx.v==1) { - dur <- data.frame(duration=signif(duration.h1h2,digits=sig.digits),hidx=idx.h1+range1+1,pidx=1) - if (is.null(alldur)) { - alldur <- dur - } else { - alldur <- rbind(alldur,dur) - } - - } else { - - for(pidx in 1:length(paths[[idx.h1+range1+1]])) { - - curdur = signif(duration.h1h2 + duras[[idx.h1+range1+1]][pidx],digits=sig.digits) - - dur <- data.frame(duration=curdur,hidx=idx.h1+range1+1,pidx=pidx) - if (is.null(alldur)) { - alldur <- dur - } else { - alldur <- rbind(alldur,dur) - } - - } - - } - - } - - if (idx.v>1) { - - if (idx.v != grid.v) { - - idx.min <- alldur[which(alldur[,"duration"] == min(alldur[,"duration"])),] - - jdx <- 0 - for (pidx in 1:nrow(idx.min)) { - jdx <- jdx+1 - paths.new[[idx.h2+grid.h+1]][[jdx]] <- c(paths[[idx.min[pidx,"hidx"]]][[idx.min[pidx,"pidx"]]],idx.h2) - duras.new[[idx.h2+grid.h+1]][[jdx]] <- idx.min[pidx,"duration"] - } - - } else { - - for(ddx in 1:nrow(alldur)) { - - tcks <- tacks(c(paths[[alldur[ddx,"hidx"]]][[alldur[ddx,"pidx"]]],0)) - alldur[ddx,"duration"] <- tcks*tack.cost + alldur[ddx,"duration"] - - } - - dur.idx <- order(alldur[,"duration"]) - srtdur <- alldur[dur.idx,] - - jdx <- 1 - kdx <- 1 - p.grps <- length(unique(alldur[,"duration"])) - paths.new <- vector("list",p.grps) - paths.new[[kdx]][[jdx]] <- c(paths[[srtdur[1,"hidx"]]][[srtdur[1,"pidx"]]],0) - duras.new <- vector("list",p.grps) - duras.new[[kdx]] <- srtdur[1,"duration"] - - for (pidx in 2:nrow(alldur)) { - - if (srtdur[pidx,"duration"]==srtdur[pidx-1,"duration"]) { - jdx <- jdx + 1 - } else { - jdx <- 1 - kdx <- kdx + 1 - duras.new[[kdx]] <- srtdur[pidx,"duration"] - } - paths.new[[kdx]][[jdx]] <- c(paths[[srtdur[pidx,"hidx"]]][[srtdur[pidx,"pidx"]]],0) - - } - - } - - } else { - for (idx in -grid.h:grid.h) { - paths.new[[idx+grid.h+1]][[1]] <- c(0,idx) - dur.eval <- duration(0,idx,1) - duras.new[[idx+grid.h+1]][[1]] <- dur.eval[1] - } - } - - } - - paths <- paths.new - duras <- duras.new - + //create adjacency graph including start and end + Map> graph = new HashMap>(); + graph.put(start, Arrays.asList(sailGrid[0])); + for(int i = 0; i < gridv-1; i++) { + for(Position p : sailGrid[i]) { + graph.put(p, Arrays.asList(sailGrid[i+1])); + } } - - */ + for(Position p : sailGrid[gridv-1]) { + graph.put(p, Arrays.asList(end)); + } + + //create backwards adjacency graph, required to reconstruct the optimal path + Map> backGraph = new HashMap>(); + backGraph.put(end, Arrays.asList(sailGrid[gridv-1])); + for(int i = gridv-1; i>0; i--) { + for(Position p: sailGrid[i]) { + backGraph.put(p, Arrays.asList(sailGrid[i-1])); + } + } + for(Position p : sailGrid[0]) { + backGraph.put(p, Arrays.asList(start)); + } + + //create tentative distance matrix + Map tentativeDistances = new HashMap(); + for(Position p : graph.keySet()) { + tentativeDistances.put(p, Long.MAX_VALUE); + } + tentativeDistances.put(start, 0L); + tentativeDistances.put(end, Long.MAX_VALUE); + + //create set of unvisited nodes + List unvisited = new ArrayList(graph.keySet()); + unvisited.add(end); + + //set the initial node as current + Position currentPosition = start; + TimePoint currentTime = startTime; + + //search loop + //ends when the end is visited + while(currentPosition != end) { + //set the polar diagram to the wind at the current position and time + TimedPosition currentTimedPosition = new TimedPositionImpl(currentTime, currentPosition); + SpeedWithBearing currentWind = windField.getWind(currentTimedPosition); + polarDiagram.setWind(currentWind); + + //compute the tentative distance to all the unvisited neighbours of the current node + //and replace it in the matrix if is smaller than the previous one + List unvisitedNeighbours = new LinkedList(graph.get(currentPosition)); + unvisitedNeighbours.retainAll(unvisited); + for(Position p : unvisitedNeighbours) { + Bearing bearingToP = currentPosition.getBearingGreatCircle(p); + Distance distanceToP = currentPosition.getDistance(p); + Speed speedToP = polarDiagram.getSpeedAtBearing(bearingToP); + //multiplied by 1000 to have milliseconds + Long timeToP = (long) (1000 * (distanceToP.getMeters() / speedToP.getMetersPerSecond())); + Long tentativeDistanceToP = currentTime.asMillis() + timeToP; + if (tentativeDistanceToP < tentativeDistances.get(p)) { + tentativeDistances.put(p, tentativeDistanceToP); + } + } + + //mark current node as visited + unvisited.remove(currentPosition); + + //select the unvisited node with the smallest tentative distance + //and set it as current + Long minTentativeDistance = Long.MAX_VALUE; + for (Position p : unvisited) { + if( tentativeDistances.get(p) < minTentativeDistance ) { + currentPosition = p; + currentTime = new MillisecondsTimePoint(minTentativeDistance); + } + + } + } + //I need to add the end point to the distances matrix + tentativeDistances.put(end,currentTime.asMillis()); + + //at this point currentPosition = end + //currentTime = total duration of the course + + //reconstruct the optimal path by going from start to end + while(currentPosition != start) { + TimedPositionWithSpeed currentTimedPositionWithSpeed = new TimedPositionWithSpeedImpl(currentTime, currentPosition, null ); + lst.addFirst(currentTimedPositionWithSpeed); + List currentPredecessors = backGraph.get(currentPosition); + Long minTime = Long.MAX_VALUE; + for(Position p : currentPredecessors) { + if(tentativeDistances.get(p) < minTime) { + minTime = tentativeDistances.get(p); + currentPosition = p; + currentTime = new MillisecondsTimePoint(minTime); + } + } + } + //I need to add the first point to the path + lst.addFirst(new TimedPositionWithSpeedImpl(startTime, start, null)); + return new PathImpl(lst); } - - @Override - public Path getOpputunisticPath() { - // TODO Auto-generated method stub - return createDummy(); - } + + @Override + public Map getAllPaths() { + Map allPaths = new HashMap(); + allPaths.put("Dummy", createDummy()); + allPaths.put("Heuristic", createHeuristic()); + allPaths.put("Djikstra", createDjikstra()); + return allPaths; + } + }