diff --git a/src/de/tud/kom/p2psim/impl/topology/PositionVector.java b/src/de/tud/kom/p2psim/impl/topology/PositionVector.java index a8fbfdc09d7e7b6acffefa0fa6e0094de17769de..7b0740c851f0b160dc9e145420608b09c615082f 100644 --- a/src/de/tud/kom/p2psim/impl/topology/PositionVector.java +++ b/src/de/tud/kom/p2psim/impl/topology/PositionVector.java @@ -23,8 +23,6 @@ package de.tud.kom.p2psim.impl.topology; import java.awt.Point; import java.util.Arrays; -import org.aopalliance.aop.AspectException; - import com.vividsolutions.jts.geom.Coordinate; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location; diff --git a/src/de/tud/kom/p2psim/impl/topology/movement/local/AbstractLocalMovementStrategy.java b/src/de/tud/kom/p2psim/impl/topology/movement/local/AbstractLocalMovementStrategy.java index 410993b11f5cb0cea87629fe0fea346f912efa23..7e56254940a1d3ec363dce1d822c964e7e1f8142 100644 --- a/src/de/tud/kom/p2psim/impl/topology/movement/local/AbstractLocalMovementStrategy.java +++ b/src/de/tud/kom/p2psim/impl/topology/movement/local/AbstractLocalMovementStrategy.java @@ -26,6 +26,7 @@ import de.tud.kom.p2psim.api.topology.obstacles.ObstacleModel; import de.tud.kom.p2psim.api.topology.waypoints.WaypointModel; import de.tud.kom.p2psim.impl.topology.PositionVector; import de.tud.kom.p2psim.impl.util.Either; +import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.RouteSensor; public abstract class AbstractLocalMovementStrategy implements LocalMovementStrategy { @@ -34,6 +35,8 @@ public abstract class AbstractLocalMovementStrategy implements protected ObstacleModel obstacleModel; + private static boolean calculateRouteSegments = false; + private double scaleFactor = 1; public double getMovementSpeed(SimLocationActuator ms) { @@ -57,6 +60,37 @@ public abstract class AbstractLocalMovementStrategy implements this.obstacleModel = model; } + protected boolean isCalculateRouteSegments() { + return calculateRouteSegments; + } + + /** + * For the visualization: programmatically enable route segment + * calculations. + */ + public static void setCalculationOfRouteSegments(boolean setting) { + calculateRouteSegments = setting; + } + + /** + * For the visualization: programmatically check route segment + * calculation. + */ + public static boolean isCalculatingRouteSegments() { + return calculateRouteSegments; + } + + /** + * If you intend to use the {@link RouteSensor} and operate on a per-segment + * basis, set this to true. If you DO NOT use the respective segment data, + * set it to false, as this option consumes significant resources. + * + * @param calculateRouteSegments + */ + public void setCalculateRouteSegments(boolean calculateRouteSegments) { + this.calculateRouteSegments = calculateRouteSegments; + } + @Override public abstract Either nextPosition( SimLocationActuator comp, PositionVector destination); diff --git a/src/de/tud/kom/p2psim/impl/topology/movement/local/OnlineMapQuestMovement.java b/src/de/tud/kom/p2psim/impl/topology/movement/local/OnlineMapQuestMovement.java index a733fa121c2dd84d0eb8875ae010d14a83c9d668..e243d3d1947e6a225d992dbcca0e1d00b094ee98 100644 --- a/src/de/tud/kom/p2psim/impl/topology/movement/local/OnlineMapQuestMovement.java +++ b/src/de/tud/kom/p2psim/impl/topology/movement/local/OnlineMapQuestMovement.java @@ -53,7 +53,7 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy { private PositionVector worldDimensions; - private static HashMap movementPoints = new HashMap<>(); + private static HashMap movementPoints = new HashMap<>(); private String movementType; // {Fastest, Shortest, Pedestrian, Bicycle} private String mapQuestKey; @@ -82,8 +82,8 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy { newPosition = destination.clone(); } else { //if not set already for this node or new destination is different than last one - PointList pointList; - if(!movementPoints.containsKey(comp.hashCode()) || destination.distanceTo(movementPoints.get(comp.hashCode()).getDestination()) > 1.0) { + RouteImpl trajectory = movementPoints.get(comp); + if(trajectory == null || destination.distanceTo(trajectory.getDestination()) > 1.0) { double[] startPosition = transformOwnWorldWindowToGPS(comp.getRealPosition().getX(), comp.getRealPosition().getY()); double[] destinationPosition = transformOwnWorldWindowToGPS(destination.getX(), destination.getY()); @@ -110,7 +110,7 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy { } //read in all waypoints - pointList = new PointList(); + PointList pointList = new PointList(); if(allDirections != null) { for(int i = 0; i < allDirections.length(); i++) { try { @@ -123,25 +123,12 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy { } } } - movementPoints.put(comp.hashCode(), new RealWorldMovementPoints(comp.getRealPosition(), destination, pointList, 0)); - } - else { - pointList = movementPoints.get(comp.hashCode()).getPointList(); + trajectory = new RouteImpl(comp.getRealPosition(), destination, pointList); + movementPoints.put(comp, trajectory); } - int actualIndex = movementPoints.get(comp.hashCode()).getActualIndex(); - int i = 0; - for(GHPoint3D temp : pointList) { - if(i==actualIndex) { - PositionVector nextPoint = transformGPSWindowToOwnWorld(temp.getLat(), temp.getLon()); - newPosition = comp.getRealPosition().moveStep(nextPoint, getMovementSpeed(comp)); - - if(nextPoint.distanceTo(comp.getRealPosition()) < maxDistanceNextPoint) actualIndex++; - } - i++; - } + newPosition = trajectory.updateCurrentLocation(comp, getMovementSpeed(comp), 1.0); - movementPoints.put(comp.hashCode(), new RealWorldMovementPoints(movementPoints.get(comp.hashCode()).getStart(), destination, pointList, actualIndex)); } return new Left(newPosition); } @@ -159,18 +146,6 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy { return gps_coordinates; } - /** - * Projects the gps coordinates in the given gps window to the world-coordinates given in world-dimensions - * @param lat - * @param lon - * @return The projected position in world-dimensions - */ - private PositionVector transformGPSWindowToOwnWorld(double lat, double lon) { - double x = worldDimensions.getX() * (lon - lonLeft)/(lonRight - lonLeft); - double y = worldDimensions.getY() - worldDimensions.getY() * (lat - latLeft)/(latRight - latLeft); - return new PositionVector(x, y); - } - public void setMovementType(String movementType) { this.movementType = movementType; } diff --git a/src/de/tud/kom/p2psim/impl/topology/movement/local/RealWorldMovementPoints.java b/src/de/tud/kom/p2psim/impl/topology/movement/local/RealWorldMovementPoints.java deleted file mode 100644 index 6b3c844c108ef2787bac9c19ec0a9f62cd456a26..0000000000000000000000000000000000000000 --- a/src/de/tud/kom/p2psim/impl/topology/movement/local/RealWorldMovementPoints.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2005-2010 KOM – Multimedia Communications Lab - * - * This file is part of PeerfactSim.KOM. - * - * PeerfactSim.KOM is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * PeerfactSim.KOM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with PeerfactSim.KOM. If not, see . - * - */ - -package de.tud.kom.p2psim.impl.topology.movement.local; - -import com.graphhopper.util.PointList; - -import de.tud.kom.p2psim.impl.topology.PositionVector; - -public class RealWorldMovementPoints { - - private PositionVector start; - private PositionVector destination; - private PointList pointList; - private int actualIndex; - - public RealWorldMovementPoints(PositionVector start, PositionVector destination, PointList pointList, int actualIndex) { - this.start = start; - this.destination = destination; - this.pointList = pointList; - this.actualIndex = actualIndex; - } - - public PositionVector getStart() { - return start; - } - public void setStart(PositionVector start) { - this.start = start; - } - public PositionVector getDestination() { - return destination; - } - public void setDestination(PositionVector destination) { - this.destination = destination; - } - public PointList getPointList() { - return pointList; - } - public void setPointList(PointList pointList) { - this.pointList = pointList; - } - public int getActualIndex() { - return this.actualIndex; - } -} diff --git a/src/de/tud/kom/p2psim/impl/topology/movement/local/RealWorldStreetsMovement.java b/src/de/tud/kom/p2psim/impl/topology/movement/local/RealWorldStreetsMovement.java index 50766cc2971296d42c3ab1ec4f07ee09d22907ae..5b92aba878aca6661c2562c7b01568b9def6b3cf 100644 --- a/src/de/tud/kom/p2psim/impl/topology/movement/local/RealWorldStreetsMovement.java +++ b/src/de/tud/kom/p2psim/impl/topology/movement/local/RealWorldStreetsMovement.java @@ -20,26 +20,37 @@ package de.tud.kom.p2psim.impl.topology.movement.local; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; + import com.graphhopper.GHRequest; import com.graphhopper.GHResponse; import com.graphhopper.GraphHopper; +import com.graphhopper.routing.util.EdgeFilter; import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.storage.index.LocationIndex; +import com.graphhopper.storage.index.QueryResult; import com.graphhopper.util.DistanceCalc2D; +import com.graphhopper.util.EdgeIteratorState; import com.graphhopper.util.PointList; import com.graphhopper.util.shapes.GHPoint; -import com.graphhopper.util.shapes.GHPoint3D; + import de.tud.kom.p2psim.api.topology.Topology; import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator; import de.tud.kom.p2psim.impl.topology.PositionVector; import de.tud.kom.p2psim.impl.topology.movement.modularosm.GPSCalculation; -import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView; +import de.tud.kom.p2psim.impl.topology.movement.modularosm.RouteSensorComponent; import de.tud.kom.p2psim.impl.util.Either; import de.tud.kom.p2psim.impl.util.Left; import de.tudarmstadt.maki.simonstrator.api.Binder; import de.tudarmstadt.maki.simonstrator.api.Monitor; import de.tudarmstadt.maki.simonstrator.api.Monitor.Level; - -import java.util.*; +import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException; /** * This movement strategy uses the data from osm and navigates the nodes throught streets to the destination @@ -52,21 +63,24 @@ import java.util.*; */ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy { - private PositionVector worldDimensions; + private static PositionVector worldDimensions; private GraphHopper hopper; + private LocationIndex index; private boolean init = false; - private static HashMap movementPoints = new HashMap<>(); - + private static HashMap currentRoutes = new HashMap<>(); + + private Map routeSensorComponents = new LinkedHashMap<>(); + private String osmFileLocation; //use pbf-format, because osm-format causes problems (xml-problems) private String graphFolderFiles; private String movementType; //car, bike or foot private String defaultMovement; private String navigationalType; //fastest, - private double latLower; //Values from -90 to 90; always smaller than latUpper - private double latUpper; //Values from -90 to 90 - private double lonLeft; //Values from -180 to 180; Always smaller than lonRight - private double lonRight; //Values from -180 to 180 + private static double latLower; //Values from -90 to 90; always smaller than latUpper + private static double latUpper; //Values from -90 to 90 + private static double lonLeft; //Values from -180 to 180; Always smaller than lonRight + private static double lonRight; //Values from -180 to 180 private boolean uniqueFolders; /** @@ -76,12 +90,13 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy { private double tolerance = 1; public RealWorldStreetsMovement() { - this.worldDimensions = Binder.getComponentOrNull(Topology.class) + worldDimensions = Binder.getComponentOrNull(Topology.class) .getWorldDimensions(); latLower = GPSCalculation.getLatLower(); latUpper = GPSCalculation.getLatUpper(); lonLeft = GPSCalculation.getLonLeft(); lonRight = GPSCalculation.getLonRight(); + } private void init() { @@ -101,28 +116,48 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy { } hopper.setEncodingManager(new EncodingManager(movementType)); hopper.importOrLoad(); + index = hopper.getLocationIndex(); init = true; } - public Either nextPosition(SimLocationActuator comp, PositionVector destination) - { + @Override + public Either nextPosition( + SimLocationActuator comp, PositionVector destination) { return nextPosition(comp, destination, defaultMovement); } public Either nextPosition(SimLocationActuator comp, PositionVector destination, String movementType) { - if(movementType == null || movementType.equals("") || !this.movementType.contains(movementType)) + if (movementType == null || movementType.equals("") + || !this.movementType.contains(movementType)) { throw new AssertionError("Invalid movement type: " + movementType); + } + + if (!init) { + init(); + } + + RouteSensorComponent routeSensor = routeSensorComponents.get(comp); + if (routeSensor == null) { + try { + routeSensorComponents.put(comp, comp.getHost() + .getComponent(RouteSensorComponent.class)); + routeSensor = routeSensorComponents.get(comp); + } catch (ComponentNotAvailableException e) { + throw new AssertionError( + "The movement model needs to provide RouteSensorComponents."); + } + } + - if(!init) init(); PositionVector newPosition = null; - if (destination.distanceTo(comp.getRealPosition()) > getMovementSpeed(comp)) - { + if (destination + .distanceTo(comp.getRealPosition()) > getMovementSpeed(comp)) { //if not set already for this node or new destination is different than last one - PointList pointList; - if(!movementPoints.containsKey(comp) || destination.distanceTo(movementPoints.get(comp).getDestination()) > 1.0) { + RouteImpl trajectory = currentRoutes.get(comp); + if(trajectory == null || destination.distanceTo(trajectory.getDestination()) > 1.0) { double[] startPosition = transformOwnWorldWindowToGPS(comp.getRealPosition().getX(), comp.getRealPosition().getY()); double[] destinationPosition = transformOwnWorldWindowToGPS(destination.getX(), destination.getY()); GHRequest req = new GHRequest(startPosition[0], startPosition[1], destinationPosition[0], destinationPosition[1]). @@ -136,41 +171,79 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy { "%f,%f), destination (%f,%f) and type %s failed with error: %s.", comp.getHost().getId().valueAsString(),startPosition[0], startPosition[1], destinationPosition[0], destinationPosition[1], movementType, rsp.getErrors()); - pointList = new PointList(); + PointList pointList = new PointList(); pointList.add(new GHPoint(destinationPosition[0], destinationPosition[1])); - movementPoints.put(comp, new RealWorldMovementPoints(comp.getRealPosition(), destination, pointList, 0)); - } - else { - pointList = rsp.getBest().getPoints(); - movementPoints.put(comp, new RealWorldMovementPoints(comp.getRealPosition(), destination, pointList, 0)); + trajectory = new RouteImpl(comp.getRealPosition(), destination, pointList); + currentRoutes.put(comp, trajectory); + + } else { + PointList pointList = rsp.getBest().getPoints(); + + if (isCalculateRouteSegments()) { + /* + * Obtain segment IDs along the route. + */ + trajectory = new RouteImpl(comp.getRealPosition(), + destination, pointList, + routeSensor.getSegmentListeners(), routeSensor, + calculateSegments(pointList)); + } else { + trajectory = new RouteImpl(comp.getRealPosition(), + destination, pointList); + } + currentRoutes.put(comp, trajectory); } + routeSensor.setNewRoute(trajectory); } - else { - pointList = movementPoints.get(comp).getPointList(); + newPosition = trajectory.updateCurrentLocation(comp, getMovementSpeed(comp), tolerance); + + if (trajectory.reachedDestination()) { + routeSensor.reachedDestination(); } - - int actualIndex = movementPoints.get(comp).getActualIndex(); - int i = 0; - for(GHPoint3D temp : pointList) { - if(i==actualIndex) { - PositionVector nextPoint = transformGPSWindowToOwnWorld(temp.getLat(), temp.getLon()); - newPosition = comp.getRealPosition().moveStep(nextPoint, getMovementSpeed(comp)); - - if (nextPoint - .distanceTo(comp.getRealPosition()) < tolerance) { - actualIndex++; + } + return new Left(newPosition); + } + + /** + * Calculate the route segments along the given point list. + * + * @return + */ + private List calculateSegments(PointList pointList) { + int ptIdx = 0; + int prevEdgeId = -1; + List segments = new LinkedList<>(); + RouteSegmentImpl lastSegment = null; + for (GHPoint point : pointList) { + QueryResult qr = index.findClosest(point.getLat(), point.getLon(), + EdgeFilter.ALL_EDGES); + EdgeIteratorState nearEdge = qr.getClosestEdge(); + if (nearEdge != null) { + int edgeId = nearEdge.getEdge(); + if (edgeId != prevEdgeId) { + if (lastSegment != null) { + lastSegment.addRouteLocation( + transformGPSWindowToOwnWorld(point.getLat(), + point.getLon())); } + lastSegment = new RouteSegmentImpl(Integer.toString(edgeId), + ptIdx, transformGPSWindowToOwnWorld(point.getLat(), + point.getLon())); + segments.add(lastSegment); + } else { + lastSegment.addRouteLocation(transformGPSWindowToOwnWorld( + point.getLat(), point.getLon())); } - i++; } - - movementPoints.put(comp, new RealWorldMovementPoints(movementPoints.get(comp).getStart(), destination, pointList, actualIndex)); + ptIdx++; } - return new Left(newPosition); + return segments; } - + /** - * Projects the world coordinates in the given gps window to the gps-coordinates + * Projects the world coordinates in the given gps window to the + * gps-coordinates + * * @param x * @param y * @return The projected position in gps-coordinates (lat, long) @@ -188,7 +261,7 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy { * @param lon * @return The projected position in world-dimensions */ - private PositionVector transformGPSWindowToOwnWorld(double lat, double lon) { + public static PositionVector transformGPSWindowToOwnWorld(double lat, double lon) { double x = worldDimensions.getX() * (lon - lonLeft)/(lonRight - lonLeft); double y = worldDimensions.getY() - worldDimensions.getY() * (lat - latLower)/(latUpper - latLower); x = Math.max(0, x); @@ -207,7 +280,7 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy { public List getMovementPoints(SimLocationActuator ms) { List positions = new LinkedList<>(); - PointList pointList = movementPoints.get(ms).getPointList(); + PointList pointList = currentRoutes.get(ms).getPointList(); pointList.forEach(p -> positions.add(new PositionVector(transformGPSWindowToOwnWorld(p.getLat(), p.getLon())))); @@ -248,7 +321,7 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy { */ public double getCurrentRouteLength(SimLocationActuator ms) { - return movementPoints.get(ms).getPointList().calcDistance(new DistanceCalc2D()); + return currentRoutes.get(ms).getPointList().calcDistance(new DistanceCalc2D()); } public void setOsmFileLocation(String osmFileLocation) { diff --git a/src/de/tud/kom/p2psim/impl/topology/movement/local/RouteImpl.java b/src/de/tud/kom/p2psim/impl/topology/movement/local/RouteImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..4f973e54197a170522d562ee514d1fd9d56d0241 --- /dev/null +++ b/src/de/tud/kom/p2psim/impl/topology/movement/local/RouteImpl.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2005-2010 KOM – Multimedia Communications Lab + * + * This file is part of PeerfactSim.KOM. + * + * PeerfactSim.KOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * PeerfactSim.KOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with PeerfactSim.KOM. If not, see . + * + */ + +package de.tud.kom.p2psim.impl.topology.movement.local; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import com.graphhopper.util.PointList; +import com.graphhopper.util.shapes.GHPoint3D; + +import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator; +import de.tud.kom.p2psim.impl.topology.PositionVector; +import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location; +import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.Route; +import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.RouteSensor; +import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.RouteSensor.RouteSegmentListener; + +/** + * This class contains one trajectory from a start to a destination. It saves + * the respective points (coordinates) along the path with a given movement + * speed and also (if enabled) the assigned IDs of the roads and walkways that + * are traversed. + * + * TODO add segment handling + * + * Segment handling is only done if there is a segment listener registered for + * the respective client. + * + * @author Bjoern Richerzhagen + * @version 1.0, Aug 9, 2017 + */ +public class RouteImpl implements Route { + + private final RouteSensor sensor; + + private PositionVector start; + + private PositionVector destination; + + private PointList pointList; + + private final List positionsList; + + private final List routeSegments; + + private final Set segmentListeners; + + private int currentIndex; + + private int currentSegmentIndex; + + private int routeLength; + + private int routeSegmentsLength; + + private boolean reachedTarget; + + /** + * + * @param start + * @param destination + * @param pointList + * @param segmentListeners + * @param routeSegments + */ + public RouteImpl(PositionVector start, + PositionVector destination, PointList pointList, + Set segmentListeners, RouteSensor sensor, + List routeSegments) { + this.sensor = sensor; + this.start = start; + this.destination = destination; + this.pointList = pointList; + this.positionsList = new LinkedList<>(); + // Create Position vectors ONCE + for (GHPoint3D temp : pointList) { + positionsList + .add(RealWorldStreetsMovement.transformGPSWindowToOwnWorld( + temp.getLat(), temp.getLon())); + } + routeLength = positionsList.size(); + this.segmentListeners = segmentListeners; + this.routeSegments = routeSegments; + this.routeSegmentsLength = routeSegments.size(); + } + + /** + * + * @param start + * @param destination + * @param pointList + */ + public RouteImpl(PositionVector start, + PositionVector destination, PointList pointList) { + this(start, destination, pointList, Collections.emptySet(), null, + Collections.emptyList()); + } + + public PositionVector getStart() { + return start; + } + + public PositionVector getDestination() { + return destination; + } + + @Override + public Location getTargetLocation() { + return destination; + } + + @Override + public boolean hasSegmentInformation() { + return routeSegmentsLength != 0; + } + + @Override + public RouteSegment getCurrentSegment() { + if (!hasSegmentInformation()) { + throw new UnsupportedOperationException( + "To access segments within a Route, you need to enable the respective functionality in the local movement strategy (usually: calculateRouteSegments=true)"); + } + return routeSegments.get(currentSegmentIndex); + } + + @Override + public List getSegmentsAhead() { + if (!hasSegmentInformation()) { + throw new UnsupportedOperationException( + "To access segments within a Route, you need to enable the respective functionality in the local movement strategy (usually: calculateRouteSegments=true)"); + } + return Collections.unmodifiableList(routeSegments + .subList(currentSegmentIndex, routeSegmentsLength)); + } + + @Override + public List getSegmentsBehind() { + if (!hasSegmentInformation()) { + throw new UnsupportedOperationException( + "To access segments within a Route, you need to enable the respective functionality in the local movement strategy (usually: calculateRouteSegments=true)"); + } + return Collections.unmodifiableList(routeSegments + .subList(0, currentSegmentIndex)); + } + + /** + * Update the internal location with the given movement speed + * + * @param speed + * @param realPosition + * @return + */ + public PositionVector updateCurrentLocation(SimLocationActuator comp, + double speed, double tolerance) { + List sublist = positionsList.subList(currentIndex, + routeLength); + PositionVector newPosition = null; + for (PositionVector candidate : sublist) { + newPosition = comp.getRealPosition().moveStep(candidate, speed); + if (candidate.distanceTo(comp.getRealPosition()) < tolerance) { + currentIndex++; + } else { + break; + } + } + + /* + * Segment handling (also inform listeners!) + */ + if (routeSegmentsLength > 0 + && routeSegmentsLength > currentSegmentIndex) { + List segmentSublist = routeSegments + .subList(currentSegmentIndex + 1, routeSegmentsLength); + for (RouteSegmentImpl candidate : segmentSublist) { + if (candidate.getFromRouteIndex() >= currentIndex) { + break; + } + currentSegmentIndex++; + segmentListeners.forEach(l -> l.onChangedRouteSegment(sensor, + this, routeSegments.get(currentSegmentIndex - 1), + candidate)); + } + } + + /* + * Reached target flag + */ + if (currentIndex >= routeLength) { + reachedTarget = true; + } + + return newPosition; + } + + /** + * True, if the trajectory is done and the node reached the target.s + * + * @return + */ + public boolean reachedDestination() { + return reachedTarget; + } + + @Override + public String toString() { + return "\n\tfrom " + start.toString() + " to " + destination.toString() + + "\n\tstep: " + currentIndex + " length: " + routeLength; + } + + public PointList getPointList() { + return pointList; + } + + public int getCurrentIndex() { + return this.currentIndex; + } + +} diff --git a/src/de/tud/kom/p2psim/impl/topology/movement/local/RouteSegmentImpl.java b/src/de/tud/kom/p2psim/impl/topology/movement/local/RouteSegmentImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..92087144136a2116a41ee9e912d17f6d5e0351fa --- /dev/null +++ b/src/de/tud/kom/p2psim/impl/topology/movement/local/RouteSegmentImpl.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2005-2010 KOM – Multimedia Communications Lab + * + * This file is part of PeerfactSim.KOM. + * + * PeerfactSim.KOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * PeerfactSim.KOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with PeerfactSim.KOM. If not, see . + * + */ + +package de.tud.kom.p2psim.impl.topology.movement.local; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location; +import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.Route.RouteSegment; + +/** + * Implementation of a {@link RouteSegment} + * + * @author Bjoern Richerzhagen + * @version 1.0, Aug 10, 2017 + */ +public class RouteSegmentImpl implements RouteSegment { + + private final String id; + + private final int fromRouteIndex; + + private final List routeLocations; + + /** + * The segment and associated points (via their index) + * + * @param id + * @param fromRouteIndex + */ + public RouteSegmentImpl(String id, int fromRouteIndex, + Location startLocation) { + this.id = id; + this.fromRouteIndex = fromRouteIndex; + this.routeLocations = new LinkedList<>(); + this.routeLocations.add(startLocation); + } + + public void addRouteLocation(Location location) { + this.routeLocations.add(location); + } + + @Override + public String getSegmentId() { + return id; + } + + @Override + public List getSegmentLocations() { + return Collections.unmodifiableList(routeLocations); + } + + public int getFromRouteIndex() { + return fromRouteIndex; + } + +} diff --git a/src/de/tud/kom/p2psim/impl/topology/movement/modularosm/ModularMovementModel.java b/src/de/tud/kom/p2psim/impl/topology/movement/modularosm/ModularMovementModel.java index 67004a75be2888d0386ab063bc13dac0592d075c..9a05d1bd4f3a175db8cb8f2b963c26ebe25e46fc 100644 --- a/src/de/tud/kom/p2psim/impl/topology/movement/modularosm/ModularMovementModel.java +++ b/src/de/tud/kom/p2psim/impl/topology/movement/modularosm/ModularMovementModel.java @@ -24,13 +24,10 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Random; import java.util.Set; import java.util.Vector; -import javax.swing.JComponent; - import de.tud.kom.p2psim.api.scenario.ConfigurationException; import de.tud.kom.p2psim.api.topology.Topology; import de.tud.kom.p2psim.api.topology.movement.MovementModel; @@ -52,7 +49,6 @@ import de.tudarmstadt.maki.simonstrator.api.EventHandler; import de.tudarmstadt.maki.simonstrator.api.Randoms; import de.tudarmstadt.maki.simonstrator.api.Time; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint; -import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.LocationActuator; /** * Modular Movement Model uses different models/strategies to create a movement @@ -117,6 +113,8 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac private Map currentTarget = new LinkedHashMap<>(); + private Map routeSensorComponents = new LinkedHashMap<>(); + private boolean initialized = false; private long timeBetweenMoveOperation = Simulator.SECOND_UNIT; @@ -128,9 +126,6 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac .getWorldDimensions(); this.rand = Randoms.getRandom(ModularMovementModel.class); - // Setting this model as default to prevent older code from braking. See Changelog. - modelVisualisation = new ModularMovementModelViz(this); - // scheduling initalization! Event.scheduleImmediately(this, null, EVENT_INIT); } @@ -142,6 +137,9 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac public void initialize() { if (!initialized) { + if (modelVisualisation == null) { + modelVisualisation = new ModularMovementModelViz(this); + } VisualizationInjector.injectComponent(modelVisualisation); if (mapVisualization != null) { VisualizationInjector.injectComponent(mapVisualization); @@ -224,8 +222,11 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac @Override public void addComponent(SimLocationActuator comp) { moveableHosts.add(comp); + if (!routeSensorComponents.containsKey(comp)) { + routeSensorComponents.put(comp, new RouteSensorComponent(comp)); + } } - + public Set getAllLocationActuators() { return moveableHosts; } diff --git a/src/de/tud/kom/p2psim/impl/topology/movement/modularosm/ModularMovementModelViz.java b/src/de/tud/kom/p2psim/impl/topology/movement/modularosm/ModularMovementModelViz.java index 1bb78e570d5eef16e4b62d08f9cc7b84f7efc026..d2f9534792899d22d437bb06bf21cd6ff218243a 100644 --- a/src/de/tud/kom/p2psim/impl/topology/movement/modularosm/ModularMovementModelViz.java +++ b/src/de/tud/kom/p2psim/impl/topology/movement/modularosm/ModularMovementModelViz.java @@ -21,12 +21,16 @@ package de.tud.kom.p2psim.impl.topology.movement.modularosm; import java.awt.AlphaComposite; +import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.RenderingHints; +import java.awt.Stroke; import java.awt.geom.Point2D; +import java.util.LinkedHashMap; +import java.util.Map; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; @@ -36,10 +40,20 @@ import javax.swing.event.ChangeListener; import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator; import de.tud.kom.p2psim.impl.topology.PositionVector; +import de.tud.kom.p2psim.impl.topology.movement.local.AbstractLocalMovementStrategy; import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView; import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector; import de.tud.kom.p2psim.impl.topology.views.visualization.ui.VisualizationComponent; +import de.tud.kom.p2psim.impl.topology.views.visualization.world.NodeVisInteractionListener; +import de.tudarmstadt.maki.simonstrator.api.Host; +import de.tudarmstadt.maki.simonstrator.api.Oracle; +import de.tudarmstadt.maki.simonstrator.api.common.graph.INodeID; +import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint; +import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location; +import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.Route; +import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.Route.RouteSegment; +import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.RouteSensor; /** * Visualization Component of the Attraction Points in the Modular Movement @@ -50,7 +64,7 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Attraction * @version 1.0, 02.07.2015 */ public class ModularMovementModelViz extends JComponent - implements VisualizationComponent { + implements VisualizationComponent, NodeVisInteractionListener { protected ModularMovementModel movementModel; @@ -58,29 +72,35 @@ public class ModularMovementModelViz extends JComponent protected boolean showNodePositions = true; + protected boolean showTrajectories = false; + + protected boolean setupCalculatesTrajectories = false; + private JMenu menu; + private Map trajectoryVisualizations = new LinkedHashMap<>(); + private final static int NODE_PAD = 2; private final static int ATTR_PAD = 5; private static Color COLOR_ATTR_POINT = Color.decode("#4A7B9D"); - public ModularMovementModelViz() - { + public ModularMovementModelViz() { init(); } - protected void init() - { + protected void init() { setBounds(0, 0, VisualizationInjector.getWorldX(), VisualizationInjector.getWorldY()); + VisualizationInjector.addInteractionListener(this); setOpaque(true); setVisible(true); menu = new JMenu("Mobility Model"); menu.add(createCheckboxAp()); menu.add(createCheckboxNodePositions()); + menu.add(createCheckboxTrajectories()); } public ModularMovementModelViz(ModularMovementModel model) { @@ -114,6 +134,28 @@ public class ModularMovementModelViz extends JComponent return checkBox; } + private JCheckBoxMenuItem createCheckboxTrajectories() { + setupCalculatesTrajectories = AbstractLocalMovementStrategy.isCalculatingRouteSegments(); + final JCheckBoxMenuItem checkBox = new JCheckBoxMenuItem( + "show node trajectories", showTrajectories); + checkBox.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + showTrajectories = checkBox.isSelected(); + if (!setupCalculatesTrajectories) { + AbstractLocalMovementStrategy + .setCalculationOfRouteSegments(showTrajectories); + } + VisualizationInjector.invalidate(); + } + }); + if (!setupCalculatesTrajectories) { + AbstractLocalMovementStrategy + .setCalculationOfRouteSegments(showTrajectories); + } + return checkBox; + } + @Override public void paint(Graphics g) { super.paintComponent(g); @@ -131,6 +173,73 @@ public class ModularMovementModelViz extends JComponent drawNodePosition(g2, comp); } } + + if (showTrajectories) { + for (TrajectoryVis tVis : trajectoryVisualizations.values()) { + tVis.drawTrajectory(g2); + } + } + } + + @Override + public void onHostClick(long hostID, boolean isActive) { + if (isActive) { + Host h = Oracle.getHostByID(INodeID.get(hostID)); + try { + RouteSensor routeSensor = h.getComponent(RouteSensor.class); + trajectoryVisualizations.put(hostID, + new TrajectoryVis(routeSensor)); + } catch (ComponentNotAvailableException e) { + // ignore. + } + + } else { + trajectoryVisualizations.remove(hostID); + } + } + + /** + * Visualizes the movement trajectory of a given node. + * + * @author Bjoern Richerzhagen + * @version 1.0, Aug 10, 2017 + */ + private class TrajectoryVis { + + private final RouteSensor routeSensor; + + public TrajectoryVis(RouteSensor routeSensor) { + this.routeSensor = routeSensor; + } + + public void drawTrajectory(Graphics2D g2) { + Route rt = routeSensor.getRoute(); + if (!rt.hasSegmentInformation()) { + return; + } + for (RouteSegment segment : rt.getSegmentsAhead()) { + Location lastLoc = null; + for (Location loc : segment.getSegmentLocations()) { + if (lastLoc == null) { + lastLoc = loc; + continue; + } + g2.setColor(Color.MAGENTA); + g2.setStroke(new BasicStroke(3.0f)); + g2.drawLine( + VisualizationInjector + .scaleValue(lastLoc.getLongitude()), + VisualizationInjector + .scaleValue(lastLoc.getLatitude()), + VisualizationInjector + .scaleValue(loc.getLongitude()), + VisualizationInjector + .scaleValue(loc.getLatitude())); + lastLoc = loc; + } + } + } + } /** diff --git a/src/de/tud/kom/p2psim/impl/topology/movement/modularosm/RouteSensorComponent.java b/src/de/tud/kom/p2psim/impl/topology/movement/modularosm/RouteSensorComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..fd065d218eca5c396c2ab40df71c86d35d24e176 --- /dev/null +++ b/src/de/tud/kom/p2psim/impl/topology/movement/modularosm/RouteSensorComponent.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2005-2010 KOM – Multimedia Communications Lab + * + * This file is part of PeerfactSim.KOM. + * + * PeerfactSim.KOM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * PeerfactSim.KOM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with PeerfactSim.KOM. If not, see . + * + */ + +package de.tud.kom.p2psim.impl.topology.movement.modularosm; + +import java.util.LinkedHashSet; +import java.util.Set; + +import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator; +import de.tudarmstadt.maki.simonstrator.api.Host; +import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.Route; +import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.RouteSensor; + +/** + * The OSM-based models provide an implementation of the {@link RouteSensor}, + * which is added as a new host component. It is created by supporting mobility + * models, registers itself with the host container and can then be retrieved + * via the host container from w.g., a local movement strategy. + * + * @author Bjoern Richerzhagen + * @version 1.0, Aug 10, 2017 + */ +public class RouteSensorComponent implements RouteSensor { + + private final SimLocationActuator actuator; + + private Route currentRoute; + + private final Set listeners; + + private final Set segmentListeners; + + public RouteSensorComponent(SimLocationActuator actuator) { + this.actuator = actuator; + this.listeners = new LinkedHashSet<>(); + this.segmentListeners = new LinkedHashSet<>(); + actuator.getHost().registerComponent(RouteSensorComponent.this); + } + + @Override + public void initialize() { + // + } + + @Override + public void shutdown() { + // + } + + public SimLocationActuator getActuator() { + return actuator; + } + + @Override + public Host getHost() { + return actuator.getHost(); + } + + @Override + public Route getRoute() { + return currentRoute; + } + + @Override + public void addRouteListener(RouteListener listener) { + listeners.add(listener); + } + + @Override + public void removeRouteListener(RouteListener listener) { + listeners.remove(listener); + } + + @Override + public void addRouteSegmentListener(RouteSegmentListener listener) { + segmentListeners.add(listener); + } + + @Override + public void removeRouteSegmentListener(RouteSegmentListener listener) { + segmentListeners.remove(listener); + } + + /* + * The following methods are used by the mobility models. + */ + + public void setNewRoute(Route newRoute) { + // notifies listeners + listeners.forEach(l -> l.onChangedRoute(this, currentRoute, newRoute)); + currentRoute = newRoute; + } + + public void reachedDestination() { + // notifies listeners + listeners.forEach(l -> l.onReachedDestination(this, currentRoute)); + } + + public Set getSegmentListeners() { + return segmentListeners; + } + +} diff --git a/src/de/tud/kom/p2psim/impl/topology/views/FiveGTopologyView.java b/src/de/tud/kom/p2psim/impl/topology/views/FiveGTopologyView.java index 374552ffeffd19aac51a90afc08d644d4fe6f91f..15ce6e3004a5aa31997de0388836bfbcb08f9026 100644 --- a/src/de/tud/kom/p2psim/impl/topology/views/FiveGTopologyView.java +++ b/src/de/tud/kom/p2psim/impl/topology/views/FiveGTopologyView.java @@ -20,6 +20,7 @@ package de.tud.kom.p2psim.impl.topology.views; +import java.util.Arrays; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; @@ -43,7 +44,6 @@ import de.tudarmstadt.maki.simonstrator.api.Time; import de.tudarmstadt.maki.simonstrator.api.component.sensor.handover.HandoverSensor; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location; import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor; -import edu.emory.mathcs.backport.java.util.Arrays; /** * This topology view offers a topology for mobile apps - mobile clients