Commit eff5d429 authored by Julian Zobel's avatar Julian Zobel 🦄
Browse files

Merge branch 'master' into 'cherry-pick-7698d9d7'

# Conflicts:
#   src/de/tud/kom/p2psim/impl/analyzer/metric/output/MetricOutputDAO.java
#   src/de/tud/kom/p2psim/impl/util/db/dao/DAO.java
parents 1c7f20ec 37020b44
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
public interface ISocialGroupMovementAnalyzer extends IAttractionBasedMovementAnalyzer {
public void onGroupForming(SocialMovementGroup group, IAttractionPoint currentDestination, IAttractionPoint targetDestination);
public void onGroupDisbanding(SocialMovementGroup group);
public void onGroupMerge(SocialMovementGroup group1, SocialMovementGroup group2);
public void onGroupWait(SocialMovementGroup group);
public void onGroupMovesToAttractionPoint(SocialMovementGroup group);
public void onGroupArriveAtAttractionPoint(SocialMovementGroup group);
public void onMoveToMeetingPoint(SocialMovementGroup group, SimLocationActuator node);
public void onMemberWaitAtMeetingPoint(SocialMovementGroup group, SimLocationActuator node);
public void onGroupEncounter(SocialMovementGroup group1, SocialMovementGroup group2);
}
...@@ -35,34 +35,38 @@ import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator; ...@@ -35,34 +35,38 @@ import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.api.topology.movement.local.LocalMovementStrategy; import de.tud.kom.p2psim.api.topology.movement.local.LocalMovementStrategy;
import de.tud.kom.p2psim.api.topology.placement.PlacementModel; import de.tud.kom.p2psim.api.topology.placement.PlacementModel;
import de.tud.kom.p2psim.impl.simengine.Simulator; import de.tud.kom.p2psim.impl.simengine.Simulator;
import de.tud.kom.p2psim.impl.topology.PositionVector; import de.tud.kom.p2psim.impl.topology.DefaultTopology;
import de.tud.kom.p2psim.impl.topology.TopologyFactory; import de.tud.kom.p2psim.impl.topology.TopologyFactory;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.IAttractionGenerator; import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.AttractionPointViz;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.IAttractionProvider;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.MobileAttractionPoint;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.mapvisualization.IMapVisualization; import de.tud.kom.p2psim.impl.topology.movement.modularosm.mapvisualization.IMapVisualization;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.ITransitionStrategy; import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.IAttractionAssigmentStrategy;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.ITransitionStrategy.AttractionAssignmentListener; import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.IAttractionAssigmentStrategy.AttractionAssignmentListener;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector; import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector;
import de.tud.kom.p2psim.impl.util.Either; import de.tud.kom.p2psim.impl.util.Either;
import de.tudarmstadt.maki.simonstrator.api.Binder; import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.Event; import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler; import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Randoms; import de.tudarmstadt.maki.simonstrator.api.Randoms;
import de.tudarmstadt.maki.simonstrator.api.Time; 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.IAttractionPoint;
/** /**
* Modular Movement Model uses different models/strategies to create a movement * Modular Movement Model uses different models/strategies to create a movement
* model. In this implementation, it has 3 different models/strategies. * model. In this implementation, it has 3 different models/strategies.
* <p> * <p>
* M0: AttractionGenerator -> Generates the {@link AttractionPoint}s and place * M0: AttractionGenerator -> Generates the {@link IAttractionPoint}s and place
* them on the map. The {@link AttractionPoint}s can't be moved, because they * them on the map. The {@link IAttractionPoint}s can't be moved, because they
* are static POIs from real-world data! * are static POIs from real-world data!
* <p> * <p>
* M1: A general {@link MovementModel} is not used, because we use static * M1: A general {@link MovementModel} is not used, because we use static
* attraction points. * attraction points.
* <p> * <p>
* M2: The {@link ITransitionStrategy}! It takes the Hosts, which should be moved * M2: The {@link IAttractionAssigmentStrategy}! It takes the Hosts, which should be moved
* around, but calculates only the assignment to the {@link AttractionPoint}s. * around, but calculates only the assignment to the {@link IAttractionPoint}s.
* It doesn't move the Hosts! It will be only assignment a new AttractionPoint! * It doesn't move the Hosts! It will be only assignment a new AttractionPoint!
* *
* <p> * <p>
...@@ -79,7 +83,7 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Attraction ...@@ -79,7 +83,7 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Attraction
* the AttractionPoints will be handled by the movement model in M1! <br> * the AttractionPoints will be handled by the movement model in M1! <br>
* Further it contains an offset for every Host, which will be added to the * Further it contains an offset for every Host, which will be added to the
* destination point (AttractionPoint), so that not all hosts, which are * destination point (AttractionPoint), so that not all hosts, which are
* assigned to one {@link AttractionPoint}, lies on the same point.<br> * assigned to one {@link IAttractionPoint}, lies on the same point.<br>
* *
* CHANGELOG * CHANGELOG
* *
...@@ -93,42 +97,49 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Attraction ...@@ -93,42 +97,49 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Attraction
*/ */
public class ModularMovementModel implements MovementModel, EventHandler, AttractionAssignmentListener { public class ModularMovementModel implements MovementModel, EventHandler, AttractionAssignmentListener {
private final int EVENT_MOVE = 1; protected final int EVENT_MOVE = 1;
private final int EVENT_INIT = 2; protected final int EVENT_INIT = 2;
protected PositionVector worldDimensions; protected PositionVector worldDimensions;
protected ITransitionStrategy transition; protected IAttractionAssigmentStrategy attractionAssigment;
protected IAttractionGenerator attractionGenerator; protected IAttractionProvider attractionProvider;
protected LocalMovementStrategy localMovementStrategy; protected LocalMovementStrategy localMovementStrategy;
protected IMapVisualization mapVisualization; protected IMapVisualization mapVisualization;
private ModularMovementModelViz modelVisualisation; protected ModularMovementModelViz modelVisualisation;
protected AttractionPointViz attractionPointViz;
protected Set<SimLocationActuator> moveableHosts = new LinkedHashSet<SimLocationActuator>(); protected LinkedHashSet<SimLocationActuator> moveableHosts = new LinkedHashSet<SimLocationActuator>();
private Map<SimLocationActuator, PositionVector> currentTarget = new LinkedHashMap<>(); protected LinkedHashMap<SimLocationActuator, PositionVector> currentTargets = new LinkedHashMap<>();
private Map<SimLocationActuator, RouteSensorComponent> routeSensorComponents = new LinkedHashMap<>(); protected LinkedHashMap<SimLocationActuator, RouteSensorComponent> routeSensorComponents = new LinkedHashMap<>();
private boolean initialized = false; protected boolean initialized = false;
private long timeBetweenMoveOperation = Simulator.SECOND_UNIT; protected long timeBetweenMoveOperation = Simulator.SECOND_UNIT;
private Random rand; protected Random rand = Randoms.getRandom(ModularMovementModel.class);
protected boolean placeNodesAtAP = false;
public ModularMovementModel() { public ModularMovementModel() {
this.worldDimensions = Binder.getComponentOrNull(Topology.class) this.worldDimensions = Binder.getComponentOrNull(Topology.class)
.getWorldDimensions(); .getWorldDimensions();
this.rand = Randoms.getRandom(ModularMovementModel.class);
// scheduling initalization! // scheduling initalization!
Event.scheduleImmediately(this, null, EVENT_INIT); Event.scheduleImmediately(this, null, EVENT_INIT);
} }
public void setPlaceNodes(boolean placeNodes) {
this.placeNodesAtAP = placeNodes;
}
/** /**
* This Method will be not called from the Components. So we call this * This Method will be not called from the Components. So we call this
...@@ -137,6 +148,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -137,6 +148,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
public void initialize() { public void initialize() {
if (!initialized) { if (!initialized) {
if (modelVisualisation == null) { if (modelVisualisation == null) {
modelVisualisation = new ModularMovementModelViz(this); modelVisualisation = new ModularMovementModelViz(this);
} }
...@@ -144,6 +156,10 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -144,6 +156,10 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
if (mapVisualization != null) { if (mapVisualization != null) {
VisualizationInjector.injectComponent(mapVisualization); VisualizationInjector.injectComponent(mapVisualization);
} }
if (attractionPointViz != null) {
System.out.println("insert AP viz");
VisualizationInjector.injectComponent(attractionPointViz);
}
checkConfiguration(); checkConfiguration();
...@@ -158,16 +174,21 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -158,16 +174,21 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
*/ */
localMovementStrategy localMovementStrategy
.setScaleFactor(timeBetweenMoveOperation / (double) Time.SECOND); .setScaleFactor(timeBetweenMoveOperation / (double) Time.SECOND);
List<AttractionPoint> attractionPoints = attractionGenerator attractionAssigment.addAttractionAssignmentListener(this);
.getAttractionPoints(); attractionAssigment.setAttractionProvider(attractionProvider);
transition.setAttractionPoints(attractionPoints);
transition.addAttractionAssignmentListener(this);
// This adds the mobile hosts (smartphones/users) to the transition // This adds the mobile hosts (smartphones/users) to the transition
// strategy // strategy
for (SimLocationActuator ms : moveableHosts) { for (SimLocationActuator ms : moveableHosts) {
transition.addComponent(ms); attractionAssigment.addComponent(ms);
if(placeNodesAtAP) {
IAttractionPoint assignment = attractionAssigment.getAssignment(ms);
ms.updateCurrentLocation(this.addOffset(new PositionVector(assignment),
(assignment.hasRadius() ? Math.max(assignment.getRadius(), 25.0) : 25.0)));
attractionAssigment.updateTargetAttractionPoint(ms, assignment);
}
} }
setTimeBetweenMoveOperations(timeBetweenMoveOperation); setTimeBetweenMoveOperations(timeBetweenMoveOperation);
...@@ -176,6 +197,12 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -176,6 +197,12 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
move(); move();
initialized = true; initialized = true;
// Inform analyzer of resolved movement
if(Monitor.hasAnalyzer(IAttractionBasedMovementAnalyzer.class)) {
Monitor.getOrNull(IAttractionBasedMovementAnalyzer.class).onAllNodeInitializationCompleted(moveableHosts);
}
} }
} }
...@@ -189,34 +216,19 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -189,34 +216,19 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
} }
@Override @Override
public void changeTargetLocation(SimLocationActuator actuator, AttractionPoint ap) { public void changeTargetLocation(SimLocationActuator actuator, IAttractionPoint ap) {
transition.updateTargetAttractionPoint(actuator, ap); attractionAssigment.updateTargetAttractionPoint(actuator, ap);
} }
@Override protected void checkConfiguration() {
public AttractionPoint getTargetLocation(SimLocationActuator actuator) {
return transition.getAssignment(actuator);
}
@Override
public Set<AttractionPoint> getAllAttractionPoints()
throws UnsupportedOperationException {
return transition.getAllAttractionPoints();
}
private void checkConfiguration() {
if (localMovementStrategy == null) { if (localMovementStrategy == null) {
throw new ConfigurationException( throw new ConfigurationException(
"LocalMovementStrategy is missing in ModularMovementModel!"); "LocalMovementStrategy is missing in ModularMovementModel!");
} }
if (transition == null) { if (attractionAssigment == null) {
throw new ConfigurationException( throw new ConfigurationException(
"TransitionStrategy is missing in ModularMovementModel!"); "TransitionStrategy is missing in ModularMovementModel!");
} }
if (attractionGenerator == null) {
throw new ConfigurationException(
"AttractionGenerator is missing in ModularMovementModel!");
}
} }
@Override @Override
...@@ -226,24 +238,11 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -226,24 +238,11 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
routeSensorComponents.put(comp, new RouteSensorComponent(comp)); routeSensorComponents.put(comp, new RouteSensorComponent(comp));
} }
} }
public Set<SimLocationActuator> getAllLocationActuators() {
return moveableHosts;
}
@Override
public void setTimeBetweenMoveOperations(long time) {
if (time > 0) {
this.timeBetweenMoveOperation = time;
} else {
throw new ConfigurationException(
"time is negative for the Move Operations");
}
}
@Override @Override
public void updatedAttractionAssignment(SimLocationActuator component, public void updatedAttractionAssignment(SimLocationActuator component,
AttractionPoint newAssignment) { IAttractionPoint newAssignment) {
/* /*
* Use this method to calculate the offset and target location for a * Use this method to calculate the offset and target location for a
* host. * host.
...@@ -253,24 +252,13 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -253,24 +252,13 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
/* /*
* Even if an AP does not have a radius, we slightly offset * Even if an AP does not have a radius, we slightly offset
*/ */
double apRadius = Math.max(newAssignment.getRadius(), 25.0); double apRadius = (newAssignment.hasRadius() ? Math.max(newAssignment.getRadius(), 25.0) : 25.0);
int tries = 0; int tries = 0;
do { do {
destination = new PositionVector(attractionCenter); destination = addOffset(attractionCenter, apRadius);
// Gaussian with std = 1 --> >99% of nodes
PositionVector offset = new PositionVector(
rand.nextGaussian() * apRadius / 3,
rand.nextGaussian() * apRadius / 3);
destination.add(offset);
// Check constraints // Check constraints
if (destination.getX() < 0.0 if (!checkBoundaries(destination)) {
|| destination.getX() > Binder
.getComponentOrNull(Topology.class)
.getWorldDimensions().getX() || destination.getY() < 0.0
|| destination.getY() > Binder
.getComponentOrNull(Topology.class)
.getWorldDimensions().getY()) {
destination = null; destination = null;
if (tries > 100) { if (tries > 100) {
throw new AssertionError("Unable to find a valid target destination within <100 tries."); throw new AssertionError("Unable to find a valid target destination within <100 tries.");
...@@ -278,18 +266,54 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -278,18 +266,54 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
} }
tries++; tries++;
} while (destination == null); } while (destination == null);
currentTarget.put(component, destination); currentTargets.put(component, destination);
} }
protected void move() { protected void move() {
for (SimLocationActuator component : moveableHosts) { for (SimLocationActuator component : moveableHosts) {
assert currentTarget.containsKey(component); assert currentTargets.containsKey(component);
doLocalMovement(component, currentTarget.get(component)); doLocalMovement(component, currentTargets.get(component));
}
for (IAttractionPoint aps : getAttractionPoints()) {
if(aps instanceof MobileAttractionPoint) {
((MobileAttractionPoint) aps).move();
}
} }
Event.scheduleWithDelay(timeBetweenMoveOperation, this, null, Event.scheduleWithDelay(timeBetweenMoveOperation, this, null,
EVENT_MOVE); EVENT_MOVE);
} }
/**
* Add a random gaussian offset to the given position. The offset will be within the given radius
* around the given position with a standard deviation of a third of the radius. Thus, the returned
* positions are more clustered around the position center.
*
* @param position
* @param radius
* @return
*/
public PositionVector addOffset(PositionVector position, double radius) {
/*
* This function will use a gaussian offset from the center. Most values are therefore more clustered around the position,
* only a few on the outskirts of the circle.
*
* For a uniform distribution, see for example https://stackoverflow.com/a/50746409
*/
PositionVector offsetPosition = new PositionVector(position);
double rad = Math.min(rand.nextGaussian() * radius / 3, radius);
double angle = rand.nextDouble() * 2 * Math.PI;
PositionVector offset = new PositionVector(rad * Math.cos(angle), rad * Math.sin(angle));
offsetPosition.add(offset);
return offsetPosition;
}
/** /**
* *
...@@ -300,41 +324,85 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -300,41 +324,85 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
* @param ms * @param ms
* @param destination * @param destination
*/ */
protected void doLocalMovement(SimLocationActuator ms, protected void doLocalMovement(SimLocationActuator ms, PositionVector destination) {
PositionVector destination) { Either<PositionVector, Boolean> either = localMovementStrategy.nextPosition(ms, destination);
Either<PositionVector, Boolean> either = localMovementStrategy
.nextPosition(ms, destination);
if (either.hasLeft()) { if (either.hasLeft()) {
ms.updateCurrentLocation(either.getLeft()); ms.updateCurrentLocation(either.getLeft());
/* if(!checkBoundaries(ms.getRealPosition())) {
* Check for negative or out of bound coordinates! System.err.println("Modular Movement Model: Host moved outside of simulated area!");
*/ }
assert ms.getRealPosition().getX() >= 0.0 }
&& ms.getRealPosition().getX() <= Binder else {
.getComponentOrNull(Topology.class) attractionAssigment.reachedAttractionPoint(ms, ms.getCurrentTargetAttractionPoint());
.getWorldDimensions().getX(); }
assert ms.getRealPosition().getY() >= 0.0 }
&& ms.getRealPosition().getY() <= Binder
.getComponentOrNull(Topology.class) /*
.getWorldDimensions().getY(); * =====================================================================================================
* === HELPER FUNCTIONS
* =====================================================================================================
*/
/**
* Notifies the user if a hosts position lies outside of the specified world size.
* Enable asserts to get the notification
*
*/
public boolean checkBoundaries(PositionVector position) {
return DefaultTopology.isWithinWorldBoundaries(position);
}
@Override
public void eventOccurred(Object content, int type) {
if (type == EVENT_INIT) {
initialize();
} else if (type == EVENT_MOVE) {
move();
}
}
/*
* =====================================================================================================
* === GETTER AND SETTER FUNCTIONS
* =====================================================================================================
*/
public LinkedHashSet<SimLocationActuator> getAllLocationActuators() {
return moveableHosts;
}
@Override
public void setTimeBetweenMoveOperations(long time) {
if (time > 0) {
this.timeBetweenMoveOperation = time;
} else { } else {
transition.reachedAttractionPoint(ms); throw new ConfigurationException(
"time is negative for the Move Operations");
} }
} }
public void setIAttractionGenerator(
IAttractionGenerator attractionGenerator) { public void setIAttractionProvider(IAttractionProvider attractionProvider) {
this.attractionGenerator = attractionGenerator; if (attractionProvider == null) {
throw new ConfigurationException(
"AttractionProvider is missing in ModularMovementModel!");
}
this.attractionProvider = attractionProvider;
} }
public void setLocalMovementStrategy( public void setLocalMovementStrategy(LocalMovementStrategy localMovementStrategy) {
LocalMovementStrategy localMovementStrategy) { if (localMovementStrategy == null) {
throw new ConfigurationException(
"LocalMovementStrategy is missing in ModularMovementModel!");
}
this.localMovementStrategy = localMovementStrategy; this.localMovementStrategy = localMovementStrategy;
} }
public void setITransitionStrategy(ITransitionStrategy transition) { public void setITransitionStrategy(IAttractionAssigmentStrategy transition) {
this.transition = transition; if (transition == null) {
throw new ConfigurationException(
"TransitionStrategy is missing in ModularMovementModel!");
}
this.attractionAssigment = transition;
} }
public void setIMapVisualization(IMapVisualization mapVisualization) { public void setIMapVisualization(IMapVisualization mapVisualization) {
...@@ -348,20 +416,20 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -348,20 +416,20 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
} }
@Override @Override
public void eventOccurred(Object content, int type) { public IAttractionPoint getTargetLocation(SimLocationActuator actuator) {
if (type == EVENT_INIT) { return attractionAssigment.getAssignment(actuator);
initialize();
} else if (type == EVENT_MOVE) {
move();
}
} }
/** /**
* Only for visualization! * Only for visualization!
* *
* @return * @return
*/ */
public List<AttractionPoint> getAttractionPoints() { public List<IAttractionPoint> getAttractionPoints() {
return new Vector<AttractionPoint>(transition.getAllAttractionPoints()); return new Vector<IAttractionPoint>(attractionProvider.getAttractionPoints());
}
public void setAttractionPointViz(AttractionPointViz viz) {
this.attractionPointViz = viz;
} }
} }
...@@ -20,27 +20,31 @@ ...@@ -20,27 +20,31 @@
package de.tud.kom.p2psim.impl.topology.movement.modularosm; package de.tud.kom.p2psim.impl.topology.movement.modularosm;
import java.awt.AlphaComposite;
import java.awt.BasicStroke; import java.awt.BasicStroke;
import java.awt.Color; import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Point; import java.awt.Point;
import java.awt.RenderingHints; import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map; import java.util.Map;
import javax.swing.JCheckBoxMenuItem; import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JMenu; import javax.swing.JMenu;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
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.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.movement.local.AbstractLocalMovementStrategy;
import de.tud.kom.p2psim.impl.topology.movement.local.RealWorldStreetsMovement;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.IAttractionProvider;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView; 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.VisualizationTopologyView.VisualizationInjector;
import de.tud.kom.p2psim.impl.topology.views.visualization.ui.VisualizationComponent; import de.tud.kom.p2psim.impl.topology.views.visualization.ui.VisualizationComponent;
...@@ -49,7 +53,7 @@ import de.tudarmstadt.maki.simonstrator.api.Host; ...@@ -49,7 +53,7 @@ import de.tudarmstadt.maki.simonstrator.api.Host;
import de.tudarmstadt.maki.simonstrator.api.Oracle; import de.tudarmstadt.maki.simonstrator.api.Oracle;
import de.tudarmstadt.maki.simonstrator.api.common.graph.INodeID; import de.tudarmstadt.maki.simonstrator.api.common.graph.INodeID;
import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException; 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.IAttractionPoint;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location; 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;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.Route.RouteSegment; import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.Route.RouteSegment;
...@@ -70,7 +74,7 @@ public class ModularMovementModelViz extends JComponent ...@@ -70,7 +74,7 @@ public class ModularMovementModelViz extends JComponent
protected boolean showAttractionPoints = true; protected boolean showAttractionPoints = true;
protected boolean showNodePositions = true; protected boolean showNodePositions = false;
protected boolean showTrajectories = false; protected boolean showTrajectories = false;
...@@ -80,9 +84,9 @@ public class ModularMovementModelViz extends JComponent ...@@ -80,9 +84,9 @@ public class ModularMovementModelViz extends JComponent
private Map<Long, TrajectoryVis> trajectoryVisualizations = new LinkedHashMap<>(); private Map<Long, TrajectoryVis> trajectoryVisualizations = new LinkedHashMap<>();
private final static int NODE_PAD = 2; private final static int NODE_SIZE = 2;
private final static int ATTR_PAD = 5; private final static int ATTR_SIZE = 5;
private static Color COLOR_ATTR_POINT = Color.decode("#4A7B9D"); private static Color COLOR_ATTR_POINT = Color.decode("#4A7B9D");
...@@ -156,6 +160,8 @@ public class ModularMovementModelViz extends JComponent ...@@ -156,6 +160,8 @@ public class ModularMovementModelViz extends JComponent
return checkBox; return checkBox;
} }
public static LinkedList<PointList> paths = new LinkedList<PointList>();
@Override @Override
public void paint(Graphics g) { public void paint(Graphics g) {
super.paintComponent(g); super.paintComponent(g);
...@@ -179,6 +185,33 @@ public class ModularMovementModelViz extends JComponent ...@@ -179,6 +185,33 @@ public class ModularMovementModelViz extends JComponent
tVis.drawTrajectory(g2); tVis.drawTrajectory(g2);
} }
} }
// g2.setColor(Color.black);
// PositionVector p1 = GPSCalculation.transformGPSWindowToOwnWorld(51.813680,8.783510);
// PositionVector p2 = GPSCalculation.transformGPSWindowToOwnWorld(51.806795,8.804239);
//
// g2.fillRect(VisualizationInjector.scaleValue(p1.getX()), VisualizationInjector.scaleValue(p1.getX()),
// VisualizationInjector.scaleValue(p2.getX()) - VisualizationInjector.scaleValue(p1.getX()),
// VisualizationInjector.scaleValue(p2.getY())- VisualizationInjector.scaleValue(p1.getY()));
//
// p1 = GPSCalculation.transformGPSWindowToOwnWorld(51.821036,8.771151);
// p2 = GPSCalculation.transformGPSWindowToOwnWorld(51.814987, 8.779090);
//
// g2.fillRect(VisualizationInjector.scaleValue(p1.getX()), VisualizationInjector.scaleValue(p1.getX()),
// VisualizationInjector.scaleValue(p2.getX() - p1.getX()),
// VisualizationInjector.scaleValue(p2.getY() - p1.getY()));
//
// for (PointList pointList : paths) {
// for (GHPoint3D temp : pointList) {
// PositionVector p = RealWorldStreetsMovement.transformGPSWindowToOwnWorld(temp.getLat(), temp.getLon());
//
// g2.fillOval(VisualizationInjector.scaleValue(p.getX()) - 2, VisualizationInjector.scaleValue(p.getY()) - 2, 4,4 );
// }
// }
} }
@Override @Override
...@@ -230,21 +263,21 @@ public class ModularMovementModelViz extends JComponent ...@@ -230,21 +263,21 @@ public class ModularMovementModelViz extends JComponent
lastLoc = loc; lastLoc = loc;
g2.drawString(segment.getSegmentId(), g2.drawString(segment.getSegmentId(),
VisualizationInjector VisualizationInjector
.scaleValue(lastLoc.getLongitude()), .scaleValue(lastLoc.getLongitudeOrX()),
VisualizationInjector VisualizationInjector
.scaleValue(lastLoc.getLatitude())); .scaleValue(lastLoc.getLatitudeOrY()));
continue; continue;
} }
g2.setStroke(new BasicStroke(3.0f)); g2.setStroke(new BasicStroke(3.0f));
g2.drawLine( g2.drawLine(
VisualizationInjector VisualizationInjector
.scaleValue(lastLoc.getLongitude()), .scaleValue(lastLoc.getLongitudeOrX()),
VisualizationInjector VisualizationInjector
.scaleValue(lastLoc.getLatitude()), .scaleValue(lastLoc.getLatitudeOrY()),
VisualizationInjector VisualizationInjector
.scaleValue(loc.getLongitude()), .scaleValue(loc.getLongitudeOrX()),
VisualizationInjector VisualizationInjector
.scaleValue(loc.getLatitude())); .scaleValue(loc.getLatitudeOrY()));
lastLoc = loc; lastLoc = loc;
} }
} }
...@@ -269,27 +302,30 @@ public class ModularMovementModelViz extends JComponent ...@@ -269,27 +302,30 @@ public class ModularMovementModelViz extends JComponent
*/ */
protected void drawAttractionPoints(Graphics2D g2) protected void drawAttractionPoints(Graphics2D g2)
{ {
for (AttractionPoint aPoint : movementModel.getAttractionPoints()) { Composite gc = g2.getComposite();
for (IAttractionPoint aPoint : movementModel.getAttractionPoints()) {
Point point = ((PositionVector) aPoint).asPoint(); Point point = ((PositionVector) aPoint).asPoint();
// draw border // draw border
g2.setColor(Color.BLACK); g2.setColor(Color.BLACK);
g2.setFont(VisualizationTopologyView.FONT_MEDIUM); g2.setFont(VisualizationTopologyView.FONT_MEDIUM);
g2.drawString(aPoint.getName(), g2.drawString(aPoint.getName(),
VisualizationInjector.scaleValue(point.x) - ATTR_PAD, VisualizationInjector.scaleValue(point.x) - g2.getFontMetrics().stringWidth(aPoint.getName()) / 2,
VisualizationInjector.scaleValue(point.y) - ATTR_PAD); VisualizationInjector.scaleValue(point.y - aPoint.getRadius() - 5) - ATTR_SIZE);
// g2.setColor(COLOR_ATTR_POINT);
// float alpha = 0.25f + (float) (aPoint.getWeight() / 2);
// g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
// g2.fillOval(
// VisualizationInjector.scaleValue(point.x) - ATTR_PAD,
// VisualizationInjector.scaleValue(point.y) - ATTR_PAD,
// ATTR_PAD * 2 + 1, ATTR_PAD * 2 + 1);
g2.setColor(COLOR_ATTR_POINT); g2.setColor(COLOR_ATTR_POINT);
float alpha = 0.25f + (float) (aPoint.getWeight() / 2); int radius = VisualizationInjector.scaleValue(aPoint.getRadius()) + ATTR_SIZE;
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
g2.fillOval(
VisualizationInjector.scaleValue(point.x) - ATTR_PAD,
VisualizationInjector.scaleValue(point.y) - ATTR_PAD,
ATTR_PAD * 2 + 1, ATTR_PAD * 2 + 1);
g2.setColor(COLOR_ATTR_POINT);
int radius = VisualizationInjector.scaleValue(aPoint.getRadius()) + ATTR_PAD;
g2.drawOval(VisualizationInjector.scaleValue(point.x) - radius, g2.drawOval(VisualizationInjector.scaleValue(point.x) - radius,
VisualizationInjector.scaleValue(point.y) - radius, VisualizationInjector.scaleValue(point.y) - radius,
radius * 2 + 1, radius * 2 + 1); radius * 2 + 1, radius * 2 + 1);
g2.setComposite(gc);
} }
} }
...@@ -303,10 +339,11 @@ public class ModularMovementModelViz extends JComponent ...@@ -303,10 +339,11 @@ public class ModularMovementModelViz extends JComponent
protected void drawNodePosition(Graphics2D g2, SimLocationActuator comp) protected void drawNodePosition(Graphics2D g2, SimLocationActuator comp)
{ {
Point2D pt = comp.getRealPosition().asPoint(); Point2D pt = comp.getRealPosition().asPoint();
g2.setColor(Color.GRAY); g2.setColor(Color.GRAY);
g2.fillOval(VisualizationInjector.scaleValue(pt.getX()) - NODE_PAD, g2.fillOval(VisualizationInjector.scaleValue(pt.getX()) - NODE_SIZE,
VisualizationInjector.scaleValue(pt.getY()) - NODE_PAD, NODE_PAD * 2 + 1, VisualizationInjector.scaleValue(pt.getY()) - NODE_SIZE,
NODE_PAD * 2 + 1); NODE_SIZE * 2, NODE_SIZE * 2);
} }
protected void setMovementModel(ModularMovementModel model) protected void setMovementModel(ModularMovementModel model)
......
...@@ -2,15 +2,15 @@ package de.tud.kom.p2psim.impl.topology.movement.modularosm; ...@@ -2,15 +2,15 @@ package de.tud.kom.p2psim.impl.topology.movement.modularosm;
import de.tud.kom.p2psim.api.topology.Topology; import de.tud.kom.p2psim.api.topology.Topology;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator; 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.RouteImpl; import de.tud.kom.p2psim.impl.topology.movement.local.RouteImpl;
import de.tud.kom.p2psim.impl.topology.movement.local.RealWorldStreetsMovement; import de.tud.kom.p2psim.impl.topology.movement.local.RealWorldStreetsMovement;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.ITransitionStrategy; import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.IAttractionAssigmentStrategy;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.util.Either; import de.tud.kom.p2psim.impl.util.Either;
import de.tudarmstadt.maki.simonstrator.api.Binder; import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.Monitor; import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.NodeDebugMonitor; import de.tudarmstadt.maki.simonstrator.api.NodeDebugMonitor;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
...@@ -18,9 +18,9 @@ import java.util.List; ...@@ -18,9 +18,9 @@ import java.util.List;
/** /**
* This class is meant to be used with the RealWorldStreetsMovement * This class is meant to be used with the RealWorldStreetsMovement
* and allows changes of the movement type and the used {@link ITransitionStrategy} mid-simulation * and allows changes of the movement type and the used {@link IAttractionAssigmentStrategy} mid-simulation
* on a per-host basis. It acts like the {@link ModularMovementModel}, but each of the {@link SimLocationActuator}s * on a per-host basis. It acts like the {@link ModularMovementModel}, but each of the {@link SimLocationActuator}s
* can have a different movement type and {@link ITransitionStrategy}. All routes and targets will be * can have a different movement type and {@link IAttractionAssigmentStrategy}. All routes and targets will be
* calculated accordingly. <BR><BR> * calculated accordingly. <BR><BR>
* *
* Originally the whole movement system within the simonstrator platform was not intended to be manipulable * Originally the whole movement system within the simonstrator platform was not intended to be manipulable
...@@ -33,12 +33,12 @@ import java.util.List; ...@@ -33,12 +33,12 @@ import java.util.List;
* So, since the movement of a person in real life could not be controlled by anyone but the person itself, * So, since the movement of a person in real life could not be controlled by anyone but the person itself,
* the access to this class is only provided from the simrunner project since it is responsible for the simulation * the access to this class is only provided from the simrunner project since it is responsible for the simulation
* of the "real-life" parts of the simonstrator platform. From within this project you have access to the * of the "real-life" parts of the simonstrator platform. From within this project you have access to the
* {@link de.tud.kom.p2psim.impl.topology.DefaultTopologyComponent} from where you can access this movement model. * {@link de.tud.kom.p2psim.impl.topology.component.DefaultTopologyComponent} from where you can access this movement model.
* If you want to use different movement types, all you have to do is * If you want to use different movement types, all you have to do is
* (besides selecting this model in your config) call the {@link #setMovementType(SimLocationActuator, String)} * (besides selecting this model in your config) call the {@link #setMovementType(SimLocationActuator, String)}
* for each of your components.<BR> * for each of your components.<BR>
* *
* The used {@link ITransitionStrategy} can be changed on runtime, too. However, the first * The used {@link IAttractionAssigmentStrategy} can be changed on runtime, too. However, the first
* TransitionStrategy specified in the config will be used as default, and will be applied if there is * TransitionStrategy specified in the config will be used as default, and will be applied if there is
* no further strategy specified for a specific host. To use multiple strategies, add them to your * no further strategy specified for a specific host. To use multiple strategies, add them to your
* config just as the first one. To set a specific strategy for a specific host, call the {@link #setTransitionForComponent(SimLocationActuator, Class)} * config just as the first one. To set a specific strategy for a specific host, call the {@link #setTransitionForComponent(SimLocationActuator, Class)}
...@@ -54,12 +54,12 @@ import java.util.List; ...@@ -54,12 +54,12 @@ import java.util.List;
public class ModularMultiTypeMovementModel extends ModularMovementModel public class ModularMultiTypeMovementModel extends ModularMovementModel
{ {
private HashMap<SimLocationActuator, String> movementTypes; private HashMap<SimLocationActuator, String> movementTypes;
private HashMap<SimLocationActuator, ITransitionStrategy> transitions; private HashMap<SimLocationActuator, IAttractionAssigmentStrategy> transitions;
private HashMap<Class, ITransitionStrategy> supportedTransitions; private HashMap<Class, IAttractionAssigmentStrategy> supportedTransitions;
private LinkedList<MultiTypeMovementListener> movementListeners = new LinkedList<>(); private LinkedList<MultiTypeMovementListener> movementListeners = new LinkedList<>();
/** /**
* Suppresses notifications to {@link de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.ITransitionStrategy.AttractionAssignmentListener}s. * Suppresses notifications to {@link de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.IAttractionAssigmentStrategy.AttractionAssignmentListener}s.
*/ */
private boolean suppressListenerNotify = false; private boolean suppressListenerNotify = false;
...@@ -77,9 +77,8 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel ...@@ -77,9 +77,8 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel
super.initialize(); super.initialize();
suppressListenerNotify = true; suppressListenerNotify = true;
for(ITransitionStrategy strategy : supportedTransitions.values()) for(IAttractionAssigmentStrategy strategy : supportedTransitions.values())
{ {
strategy.setAttractionPoints(transition.getAllAttractionPoints());
strategy.addAttractionAssignmentListener(this); strategy.addAttractionAssignmentListener(this);
for (SimLocationActuator ms : moveableHosts) { for (SimLocationActuator ms : moveableHosts) {
strategy.addComponent(ms); strategy.addComponent(ms);
...@@ -94,8 +93,12 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel ...@@ -94,8 +93,12 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel
assert localMovementStrategy instanceof RealWorldStreetsMovement: "ModularMultiTypeMovementModel can only be used with RealWorldStreetsMovement!"; assert localMovementStrategy instanceof RealWorldStreetsMovement: "ModularMultiTypeMovementModel can only be used with RealWorldStreetsMovement!";
Either<PositionVector, Boolean> either; Either<PositionVector, Boolean> either;
if(movementTypes.containsKey(ms)) either = ((RealWorldStreetsMovement) localMovementStrategy).nextPosition(ms, destination, movementTypes.get(ms)); if(movementTypes.containsKey(ms)) {
else either = localMovementStrategy.nextPosition(ms, destination); either = ((RealWorldStreetsMovement) localMovementStrategy).nextPosition(ms, destination, movementTypes.get(ms));
}
else {
either = localMovementStrategy.nextPosition(ms, destination);
}
if (either.hasLeft()) { if (either.hasLeft()) {
ms.updateCurrentLocation(either.getLeft()); ms.updateCurrentLocation(either.getLeft());
...@@ -112,10 +115,10 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel ...@@ -112,10 +115,10 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel
.getWorldDimensions().getY(); .getWorldDimensions().getY();
} else { } else {
if(transitions.containsKey(ms)) { if(transitions.containsKey(ms)) {
transitions.get(ms).reachedAttractionPoint(ms); transitions.get(ms).reachedAttractionPoint(ms, ms.getCurrentTargetAttractionPoint());
} }
else { else {
transition.reachedAttractionPoint(ms); attractionAssigment.reachedAttractionPoint(ms, ms.getCurrentTargetAttractionPoint());
} }
movementListeners.forEach(l -> l.onTransition(ms)); movementListeners.forEach(l -> l.onTransition(ms));
} }
...@@ -147,7 +150,7 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel ...@@ -147,7 +150,7 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel
* @param ms the component * @param ms the component
* @return the current transition strategy * @return the current transition strategy
*/ */
public ITransitionStrategy getTransitionForComponent(SimLocationActuator ms) public IAttractionAssigmentStrategy getTransitionForComponent(SimLocationActuator ms)
{ {
return transitions.get(ms); return transitions.get(ms);
} }
...@@ -157,9 +160,9 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel ...@@ -157,9 +160,9 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel
* @param strategy The class of the strategy which should be returned. * @param strategy The class of the strategy which should be returned.
* @return The specified strategy * @return The specified strategy
*/ */
public ITransitionStrategy getTransitionStrategy(Class strategy) public IAttractionAssigmentStrategy getTransitionStrategy(Class strategy)
{ {
ITransitionStrategy selectedStrategy = supportedTransitions.get(strategy); IAttractionAssigmentStrategy selectedStrategy = supportedTransitions.get(strategy);
if(selectedStrategy == null) if(selectedStrategy == null)
{ {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
...@@ -170,7 +173,7 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel ...@@ -170,7 +173,7 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel
} }
/** /**
* Sets the {@link ITransitionStrategy} for the specified {@link SimLocationActuator}. Used strategies * Sets the {@link IAttractionAssigmentStrategy} for the specified {@link SimLocationActuator}. Used strategies
* need to be registered in the config. * need to be registered in the config.
* @param ms The SimLocationActuator * @param ms The SimLocationActuator
* @param strategy the strategy to use * @param strategy the strategy to use
...@@ -185,9 +188,9 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel ...@@ -185,9 +188,9 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel
* @param ms The SimLocationActuator * @param ms The SimLocationActuator
* @param newStrategy the new strategy to use * @param newStrategy the new strategy to use
*/ */
private void changeTransitionStrategy(SimLocationActuator ms, ITransitionStrategy newStrategy) private void changeTransitionStrategy(SimLocationActuator ms, IAttractionAssigmentStrategy newStrategy)
{ {
ITransitionStrategy usedStrategy = transitions.containsKey(ms) ? transitions.get(ms) : transition; IAttractionAssigmentStrategy usedStrategy = transitions.containsKey(ms) ? transitions.get(ms) : attractionAssigment;
newStrategy.updateTargetAttractionPoint(ms, usedStrategy.getAssignment(ms)); newStrategy.updateTargetAttractionPoint(ms, usedStrategy.getAssignment(ms));
transitions.put(ms, newStrategy); transitions.put(ms, newStrategy);
Monitor.log(ModularMultiTypeMovementModel.class, Monitor.Level.DEBUG, String.format("Client %s changed his transition strategy from %s to %s", ms.getHost().getId().toString(), usedStrategy.getClass(), newStrategy.getClass())); Monitor.log(ModularMultiTypeMovementModel.class, Monitor.Level.DEBUG, String.format("Client %s changed his transition strategy from %s to %s", ms.getHost().getId().toString(), usedStrategy.getClass(), newStrategy.getClass()));
...@@ -210,7 +213,7 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel ...@@ -210,7 +213,7 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel
} }
/** /**
* Sets the default {@link ITransitionStrategy} for the specified {@link SimLocationActuator}. * Sets the default {@link IAttractionAssigmentStrategy} for the specified {@link SimLocationActuator}.
* @param ms The SimLocationActuator * @param ms The SimLocationActuator
*/ */
public void returnToDefaultTransition(SimLocationActuator ms) public void returnToDefaultTransition(SimLocationActuator ms)
...@@ -219,25 +222,25 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel ...@@ -219,25 +222,25 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel
} }
@Override @Override
public void changeTargetLocation(SimLocationActuator actuator, AttractionPoint ap) { public void changeTargetLocation(SimLocationActuator actuator, IAttractionPoint ap) {
if(transitions.containsKey(actuator)) transitions.get(actuator).updateTargetAttractionPoint(actuator, ap); if(transitions.containsKey(actuator)) transitions.get(actuator).updateTargetAttractionPoint(actuator, ap);
else transition.updateTargetAttractionPoint(actuator, ap); else attractionAssigment.updateTargetAttractionPoint(actuator, ap);
} }
@Override @Override
public void setITransitionStrategy(ITransitionStrategy transition) { public void setITransitionStrategy(IAttractionAssigmentStrategy transition) {
if(supportedTransitions.size() == 0) this.transition = transition; if(supportedTransitions.size() == 0) this.attractionAssigment = transition;
supportedTransitions.put(transition.getClass(), transition); supportedTransitions.put(transition.getClass(), transition);
} }
@Override @Override
public AttractionPoint getTargetLocation(SimLocationActuator actuator) { public IAttractionPoint getTargetLocation(SimLocationActuator actuator) {
if(transitions.containsKey(actuator)) return transitions.get(actuator).getAssignment(actuator); if(transitions.containsKey(actuator)) return transitions.get(actuator).getAssignment(actuator);
else return transition.getAssignment(actuator); else return attractionAssigment.getAssignment(actuator);
} }
@Override @Override
public void updatedAttractionAssignment(SimLocationActuator component, AttractionPoint newAssignment) { public void updatedAttractionAssignment(SimLocationActuator component, IAttractionPoint newAssignment) {
//Notifications of listeners get suppressed in setup phase to prevent multiple assignments of destinations. //Notifications of listeners get suppressed in setup phase to prevent multiple assignments of destinations.
if(suppressListenerNotify) return; if(suppressListenerNotify) return;
super.updatedAttractionAssignment(component, newAssignment); super.updatedAttractionAssignment(component, newAssignment);
......
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
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;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.api.topology.movement.local.LocalMovementStrategy;
import de.tud.kom.p2psim.api.topology.placement.PlacementModel;
import de.tud.kom.p2psim.impl.simengine.Simulator;
import de.tud.kom.p2psim.impl.topology.DefaultTopology;
import de.tud.kom.p2psim.impl.topology.TopologyFactory;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.mapvisualization.IMapVisualization;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector;
import de.tud.kom.p2psim.impl.topology.waypoints.RandomWaypointGenerator;
import de.tud.kom.p2psim.impl.util.Either;
import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Randoms;
import de.tudarmstadt.maki.simonstrator.api.Time;
public class SimpleMovementModel implements MovementModel, EventHandler {
protected final int EVENT_MOVE = 1;
protected final int EVENT_INIT = 2;
protected PositionVector worldDimensions;
protected LocalMovementStrategy localMovementStrategy;
// TODO more generic approach
protected RandomWaypointGenerator attractionGenerator;
protected IMapVisualization mapVisualization;
protected Set<SimLocationActuator> moveableHosts = new LinkedHashSet<SimLocationActuator>();
protected Map<SimLocationActuator, PositionVector> currentTargets = new LinkedHashMap<>();
protected Map<SimLocationActuator, RouteSensorComponent> routeSensorComponents = new LinkedHashMap<>();
protected boolean initialized = false;
protected long timeBetweenMoveOperation = Simulator.SECOND_UNIT;
protected Random rand = Randoms.getRandom(SimpleMovementModel.class);
public SimpleMovementModel() {
this.worldDimensions = Binder.getComponentOrNull(Topology.class)
.getWorldDimensions();
this.attractionGenerator = new RandomWaypointGenerator();
// scheduling initalization!
Event.scheduleImmediately(this, null, EVENT_INIT);
}
/**
* This Method will be not called from the Components. So we call this
* manually!
*/
public void initialize() {
if (!initialized) {
if (mapVisualization != null) {
VisualizationInjector.injectComponent(mapVisualization);
}
checkConfiguration();
// setWayPointModel
localMovementStrategy.setObstacleModel(Binder
.getComponentOrNull(Topology.class).getObstacleModel());
localMovementStrategy.setWaypointModel(Binder
.getComponentOrNull(Topology.class).getWaypointModel());
/*
* Scale depending on calculation interval, if interval != 1 Second.
*/
localMovementStrategy
.setScaleFactor(timeBetweenMoveOperation / (double) Time.SECOND);
setTimeBetweenMoveOperations(timeBetweenMoveOperation);
for (SimLocationActuator component : moveableHosts) {
updatedTargetAssignment(component, attractionGenerator.getRandomPoint());
}
// initial move
move();
initialized = true;
}
}
/**
* This default implementation relies on {@link PlacementModel}s to be
* configured in the {@link TopologyFactory}
*/
@Override
public void placeComponent(SimLocationActuator actuator) {
// not supported
}
protected void checkConfiguration() {
if (localMovementStrategy == null) {
throw new ConfigurationException(
"LocalMovementStrategy is missing in ModularMovementModel!");
}
if (attractionGenerator == null) {
throw new ConfigurationException(
"AttractionGenerator is missing in ModularMovementModel!");
}
}
@Override
public void addComponent(SimLocationActuator comp) {
moveableHosts.add(comp);
if (!routeSensorComponents.containsKey(comp)) {
routeSensorComponents.put(comp, new RouteSensorComponent(comp));
}
}
public void updatedTargetAssignment(SimLocationActuator component,
PositionVector target) {
currentTargets.put(component, target);
}
protected void move() {
for (SimLocationActuator component : moveableHosts) {
assert currentTargets.containsKey(component);
doLocalMovement(component, currentTargets.get(component));
}
Event.scheduleWithDelay(timeBetweenMoveOperation, this, null,
EVENT_MOVE);
}
/**
*
* Ask the local movement strategy for the next position. It may return the
* next position or a boolean with true to notify the movement model that it
* can't get any closer to the current way point.
*
* @param ms
* @param destination
*/
protected void doLocalMovement(SimLocationActuator ms, PositionVector destination) {
Either<PositionVector, Boolean> either = localMovementStrategy.nextPosition(ms, destination);
if (either.hasLeft()) {
ms.updateCurrentLocation(either.getLeft());
if(!checkBoundaries(ms.getRealPosition())) {
System.err.println("Modular Movement Model: Host moved outside of simulated area!");
}
}
else {
updatedTargetAssignment(ms, attractionGenerator.getRandomPoint());
}
}
/*
* =====================================================================================================
* === HELPER FUNCTIONS
* =====================================================================================================
*/
/**
* Notifies the user if a hosts position lies outside of the specified world size.
* Enable asserts to get the notification
*
*/
public boolean checkBoundaries(PositionVector position) {
return DefaultTopology.isWithinWorldBoundaries(position);
}
@Override
public void eventOccurred(Object content, int type) {
if (type == EVENT_INIT) {
initialize();
} else if (type == EVENT_MOVE) {
move();
}
}
/*
* =====================================================================================================
* === GETTER AND SETTER FUNCTIONS
* =====================================================================================================
*/
public Set<SimLocationActuator> getAllLocationActuators() {
return moveableHosts;
}
@Override
public void setTimeBetweenMoveOperations(long time) {
if (time > 0) {
this.timeBetweenMoveOperation = time;
} else {
throw new ConfigurationException(
"time is negative for the Move Operations");
}
}
public void setLocalMovementStrategy(LocalMovementStrategy localMovementStrategy) {
if (localMovementStrategy == null) {
throw new ConfigurationException(
"LocalMovementStrategy is missing in ModularMovementModel!");
}
this.localMovementStrategy = localMovementStrategy;
}
public void setIMapVisualization(IMapVisualization mapVisualization) {
this.mapVisualization = mapVisualization;
}
}
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.api.topology.Topology;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.api.topology.movement.local.LocalMovementStrategy;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.MovementGroupContainer;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupencounter.IGroupEncounterBehavior;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupforming.IGroupFormingBehavior;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.IAttractionAssigmentStrategy;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector;
import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
/**
*
* @author Julian Zobel
* @version 1.0, 28.01.2020
*/
public class SocialGroupMovementModel extends ModularMovementModel {
private final double MEETING_POINT_DISTANCE = 5;
private final double LEADER_GROUP_DISTANCE = 10;
protected MovementGroupContainer groupContainer;
protected IGroupFormingBehavior groupFormingBehavior;
protected IGroupEncounterBehavior groupEncounterBehavior;
private LinkedHashSet<SimLocationActuator> singleHosts = new LinkedHashSet<SimLocationActuator>();
private int numberOfSingleHosts;
@Override
public void initialize() {
if (!initialized) {
groupContainer = MovementGroupContainer.getInstance();
groupFormingBehavior.initialize(this);
groupEncounterBehavior.initialize(this);
// Choose single hosts
if(numberOfSingleHosts > 0) {
do {
singleHosts.add(getRandomActuator());
} while(singleHosts.size() < numberOfSingleHosts);
}
if (modelVisualisation == null) {
modelVisualisation = new ModularMovementModelViz(this);
}
VisualizationInjector.injectComponent(modelVisualisation);
if (mapVisualization != null) {
VisualizationInjector.injectComponent(mapVisualization);
}
if (attractionPointViz != null) {
System.out.println("insert AP viz");
VisualizationInjector.injectComponent(attractionPointViz);
}
checkConfiguration();
// setWayPointModel
localMovementStrategy.setObstacleModel(Binder
.getComponentOrNull(Topology.class).getObstacleModel());
localMovementStrategy.setWaypointModel(Binder
.getComponentOrNull(Topology.class).getWaypointModel());
/*
* Scale depending on calculation interval, if interval != 1 Second.
*/
localMovementStrategy
.setScaleFactor(timeBetweenMoveOperation / (double) Time.SECOND);
attractionAssigment.addAttractionAssignmentListener(this);
attractionAssigment.setAttractionProvider(attractionProvider);
// This adds the mobile hosts (smartphones/users) to the transition
// strategy
for (SimLocationActuator ms : moveableHosts) {
attractionAssigment.addComponent(ms);
if(placeNodesAtAP) {
IAttractionPoint assignment = attractionAssigment.getAssignment(ms);
ms.updateCurrentLocation(this.addOffset(new PositionVector(assignment),
(assignment.hasRadius() ? Math.max(assignment.getRadius(), 25.0) : 25.0)));
attractionAssigment.updateTargetAttractionPoint(ms, assignment);
}
}
setTimeBetweenMoveOperations(timeBetweenMoveOperation);
// Inform analyzer of resolved movement
if(Monitor.hasAnalyzer(IAttractionBasedMovementAnalyzer.class)) {
Monitor.getOrNull(IAttractionBasedMovementAnalyzer.class).onAllNodeInitializationCompleted(moveableHosts);
}
// initial move
move();
initialized = true;
}
}
/**
* Returns a random SimLocationActuator component from the set of moveable hosts.
*
* @return SimLocationActuator
*/
private SimLocationActuator getRandomActuator() {
int index = rand.nextInt(moveableHosts.size());
int i = 0;
for(SimLocationActuator actuator : moveableHosts) {
if(i == index) {
return actuator;
}
i++;
}
return null;
}
/**
* The movement model.
* 1) Updates the number of hosts within each attraction point.
* 2) Creates and deletes groups, if necessary.
* 3) Applies encounter action to meeting groups.
* 4) Moves the hosts using doGroupMovement, movement style depends on role of the host (Leader, Participant, Single)
*/
@Override
protected void move() {
// Check POIs if groups can be created. Delete groups, if destination reached.
groupFormingBehavior.manageGroups();
// Checks if groups encounter each other and applies a specified action to these groups.
groupEncounterBehavior.handleEncounters();
/*
* Moves all nodes according to definition in group movement method
*/
for (SimLocationActuator host : moveableHosts) {
assert currentTargets.containsKey(host);
// Single Host Movement
if(singleHosts.contains(host) || !groupContainer.isGroupMember(host)) {
doLocalMovement(host, currentTargets.get(host));
}
else if(groupContainer.isGroupMember(host)){
doGroupMovement(host, currentTargets.get(host));
}
else {
throw new UnsupportedOperationException("SimLocationActuator " + host + " is neither in a group nor a single node");
}
// TODO maybe remodel the group movement to do a whole group?
}
// TODO Move each group
Event.scheduleWithDelay(timeBetweenMoveOperation, this, null, EVENT_MOVE);
}
/**
* Applies movement of the host according to different circumstances:
*
* Towards Destination: Host is not a group member || Group has met at meeting point successfully.
* Towards MeetingPoint: Host if group member and has not reached meeting point yet.
* No movement: Host reached meeting point, but still waits for group members to arrive.
*
* @param SimLocationActuator The host to move.
* @param PositionVector Destination of the host.
*/
protected void doGroupMovement(SimLocationActuator host, PositionVector destination) {
// This host is a member of a group, thus...
SocialMovementGroup group = groupContainer.getGroupOfHost(host);
// if the group is currently gathering at a meeting point...
if(movesToGroupMeetingPoint(host)) {
// if this host has already arrived at the group meeting point..
if(hostIsAtMeetingPoint(host)) {
// ... and if the whole group is also at the meeting point...
if(groupGatheredAtMeetingPoint(group)) {
// remove the meeting point and thus, start moving towards the actual destination
group.setMeetingPoint(null);
}
// ... but the group is not gathered at the meeting point, do nothing and wait for the rest.
else {
// inform analyzer of waiting group members
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onMemberWaitAtMeetingPoint(group, host);
}
}
}
// if the host has not reached the group meeting point, move towards it.
else {
PositionVector mp = groupContainer.getGroupOfHost(host).getMeetingPoint();
doLocalMovement(host, mp);
// inform analyzer group member moves to meeting point
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onMoveToMeetingPoint(group, host);
}
}
}
// ... but if the group is currently on their way to the destination
else {
// the leader does a local movement
if(groupContainer.isLeader(host)) {
if(!groupContainer.isWaiting(groupContainer.getGroupOfHost(host))) {
// inform analyzer of movement to attraction point
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onGroupMovesToAttractionPoint(group);
}
doLocalMovement(host, destination);
PositionVector leaderPos = host.getRealPosition();
LinkedHashSet<SimLocationActuator> groupMembers = groupContainer.getGroupMembers(host);
groupMembers.remove(host); // remove leader
for (SimLocationActuator groupMember : groupMembers) {
// Assign small offset to the host depending on the leaders position.
PositionVector offset = new PositionVector(rand.nextDouble() * LEADER_GROUP_DISTANCE, rand.nextDouble() * LEADER_GROUP_DISTANCE);
PositionVector newPos = leaderPos.plus(offset);
// Update location of host, which will be around the leaders location.
groupMember.updateCurrentLocation(newPos);
}
}
else {
// inform analyzer of waiting group
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onGroupWait(group);
}
}
}
}
}
/*
* =====================================================================================================
* === MEETING POINT FUNCTIONS
* =====================================================================================================
*/
/**
* Checks if a host is currently on its way to a meeting point or to its destination.
* Returns true if moving towards meeting point, false else.
*
* @param SimLocationActuator The host to be checked.
* @return Boolean
*/
private boolean movesToGroupMeetingPoint(SimLocationActuator host) {
PositionVector meetingpoint = groupContainer.getGroupOfHost(host).getMeetingPoint();
if(meetingpoint == null)
return false;
else if(meetingpoint != groupContainer.getGroupOfHost(host).getDestination())
return true;
else
return false;
}
/**
* Returns true, if all group members of a host including the host itself reached the meeting point, false else.
*
* @param SimLocationActuator The host to be checked.
* @return Boolean
*/
private boolean groupGatheredAtMeetingPoint(SocialMovementGroup group) {
for(SimLocationActuator participant : group.getMembers()) {
if(!hostIsAtMeetingPoint(participant)) {
return false;
}
}
return true;
}
/**
* Returns true if the host reached its current meeting point, false else.
*
* @param SimLocationActuator The host to be checked.
* @return Boolean
*
*/
private boolean hostIsAtMeetingPoint(SimLocationActuator host) {
PositionVector meetingpoint = groupContainer.getGroupOfHost(host).getMeetingPoint();
if(host.getLastLocation().distanceTo(meetingpoint) <= MEETING_POINT_DISTANCE)
return true;
else
return false;
}
/*
* =====================================================================================================
* === MOVEMENT FUNCTIONS
* =====================================================================================================
*/
/**
* Host position adapts to leaders position by taking the leader position and adding a small offset
*
* @param SimLocationActuator The host to be moved.
*/
private void followLeader(SimLocationActuator host) {
SimLocationActuator leader = groupContainer.getGroupOfHost(host).getLeader();
// Assign small offset to the host depending on the leaders position.
PositionVector leaderPos = leader.getRealPosition();
PositionVector offset = new PositionVector(rand.nextDouble() * LEADER_GROUP_DISTANCE, rand.nextDouble() * LEADER_GROUP_DISTANCE);
PositionVector newPos = leaderPos.plus(offset);
// Update location of host, which will be around the leaders location.
host.updateCurrentLocation(newPos);
}
/*
* =====================================================================================================
* === GETTER AND SETTER FUNCTIONS
* =====================================================================================================
*/
public void setGroupFormingBehavior(IGroupFormingBehavior defaultGroupForming) {
if (defaultGroupForming == null) {
throw new ConfigurationException(
"GroupFormingStrategy is missing in ModularMovementModel!");
}
this.groupFormingBehavior = defaultGroupForming;
}
public void setGroupEncounterBehavior(IGroupEncounterBehavior defaultGroupEncounterBehavior) {
if (defaultGroupEncounterBehavior == null) {
throw new ConfigurationException(
"GroupEncounterStrategy is missing in ModularMovementModel!");
}
this.groupEncounterBehavior = defaultGroupEncounterBehavior;
}
public IAttractionAssigmentStrategy getAttractionAssignmentStrategy() {
return attractionAssigment;
}
public IGroupFormingBehavior getGroupFormingBehavior() {
return groupFormingBehavior;
}
public LocalMovementStrategy getMovementStrategy() {
return localMovementStrategy;
}
public void setNumberOfSingleHosts(int numberOfSingleHosts) {
this.numberOfSingleHosts = numberOfSingleHosts;
}
public LinkedHashSet<SimLocationActuator> getSingleHosts(){
return singleHosts;
}
public LinkedHashMap<SimLocationActuator, PositionVector> getCurrentTargets(){
return currentTargets;
}
}
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import java.util.LinkedList;
import java.util.List;
import de.tud.kom.p2psim.api.topology.Topology;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.util.oracle.GlobalOracle;
import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Monitor.Level;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
public class AbstractAttractionProvider implements IAttractionProvider {
private LinkedList<AttractionPoint> attractionPoints = new LinkedList<>();
protected PositionVector worldDimension;
public AbstractAttractionProvider() {
this.worldDimension = Binder.getComponentOrNull(Topology.class).getWorldDimensions();
}
public LinkedList<AttractionPoint> getAttractionPoints() {
return new LinkedList<AttractionPoint>(attractionPoints);
}
protected void clearAttractionPoints() {
attractionPoints.clear();
}
protected boolean hasAttractionPoint(AttractionPoint ap) {
return attractionPoints.contains(ap);
}
/**
* Remove an {@link IAttractionPoint} from the list
* @param ap
*/
public void removeAttractionPoint(AttractionPoint ap) {
if(Monitor.hasAnalyzer(AttractionPointMonitor.class)) {
Monitor.getOrNull(AttractionPointMonitor.class).removedAttractionPoint(ap);
}
GlobalOracle.removeAttractionPoint(ap);
attractionPoints.remove(ap);
}
/**
* Add an {@link IAttractionPoint} to the list
* @param ap
*/
public void addAttractionPoint(AttractionPoint ap) {
if(ap == null) {
Monitor.log(IAttractionProvider.class, Level.ERROR, "IAttractionGenerator: Tried to add NULL as Attraction Point");
return;
}
if(Monitor.hasAnalyzer(AttractionPointMonitor.class)) {
Monitor.getOrNull(AttractionPointMonitor.class).addedAttractionPoint(ap);
}
GlobalOracle.addAttractionPoint(ap);
attractionPoints.add(ap);
}
}
...@@ -21,71 +21,67 @@ ...@@ -21,71 +21,67 @@
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction; package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import de.tud.kom.p2psim.impl.topology.movement.modularosm.IAttractionBasedMovementAnalyzer;
import java.util.Random; import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tud.kom.p2psim.impl.topology.PositionVector; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
import de.tudarmstadt.maki.simonstrator.api.Randoms; import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint;
/** /**
* In the current implementation, {@link AttractionPoint}s cannot move anymore. * In the current implementation, {@link IAttractionPoint}s cannot move anymore.
* We hold a static list of attraction points to ensure that there exists only * We hold a static list of attraction points to ensure that there exists only
* one instance of each name at a time. * one instance of each name at a time.
* *
* @author Christoph Muenker, Bjoern Richerzhagen * @author Christoph Muenker, Bjoern Richerzhagen, Julian Zobel
* @version 1.0, 02.07.2013 * @version 1.0, 02.07.2013
* @version 1.1, 26.10.2018 - since the class was very similar to {@link BasicAttractionPoint}, made this class an extension
* @version 1.2, 24.01.2020 - added pause time interval, weight, and radius exclusively to this class, because {@link BasicAttractionPoint} should be just a named point.
*/ */
public class AttractionPointImpl extends PositionVector public class AttractionPoint extends BasicAttractionPoint {
implements AttractionPoint { protected static LinkedHashMap<String, AttractionPoint> instances = new LinkedHashMap<>();
protected static Random rnd = Randoms.getRandom(AttractionPoint.class);
private static Map<String, AttractionPointImpl> instances = new LinkedHashMap<>();
private String name;
private double weight = 0; protected long pauseTimeMin = -1;
protected long pauseTimeMax = -1;
protected double weight = 0;
protected double radius = 0;
protected double area = 1;
private double radius = 0; @XMLConfigurableConstructor({ "name", "x", "y" })
public AttractionPoint(String name, double x, double y) {
public AttractionPointImpl(String name, PositionVector posVec) { this(name, new PositionVector(x, y));
super(posVec); }
this.name = name;
public AttractionPoint(String name, PositionVector posVec) {
super(name, posVec);
if (instances.containsKey(name)) { if (instances.containsKey(name)) {
throw new AssertionError("Name "+name+" already in use for an attraction point."); throw new AssertionError("Name "+name+" already in use for an attraction point.");
} }
instances.put(name, this); instances.put(name, this);
}
if(Monitor.hasAnalyzer(IAttractionBasedMovementAnalyzer.class)) {
@Override Monitor.getOrNull(IAttractionBasedMovementAnalyzer.class).onAttractionPointAdded(this);
public String getName() { }
return name; }
}
public AttractionPoint(String name, PositionVector posVec, double weight, double radius, long pauseTimeMin, long pauseTimeMax) {
@Override this(name, posVec);
public double getWeight() {
return weight; assert weight >= 0 && weight <= 1.0;
} assert radius >= 0;
@Override
public double getRadius() {
return radius;
}
@Override
public void setWeight(double weight) {
this.weight = weight; this.weight = weight;
}
@Override
public void setRadius(double radius) {
this.radius = radius; this.radius = radius;
this.setPauseTime(pauseTimeMin, pauseTimeMax);
if(radius > 0) {
this.area = Math.PI * Math.pow(radius, 2);
}
} }
@Override @Override
public AttractionPoint clone(String newName) { public IAttractionPoint clone(String newName) {
return new AttractionPointImpl(name, this); return new AttractionPoint(name, this, weight, radius, pauseTimeMin, pauseTimeMax);
} }
@Override @Override
...@@ -102,17 +98,98 @@ public class AttractionPointImpl extends PositionVector ...@@ -102,17 +98,98 @@ public class AttractionPointImpl extends PositionVector
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if(obj == null)
return false;
if (this == obj) if (this == obj)
return true; return true;
if (getClass() != obj.getClass()) if (getClass() != obj.getClass())
return false; return false;
AttractionPointImpl other = (AttractionPointImpl) obj; AttractionPoint other = (AttractionPoint) obj;
if (name == null) { if (name == null) {
if (other.name != null) if (other.name != null)
return false; return false;
} else if (!name.equals(other.name)) } else if (!name.equals(other.name)) {
return false; return false;
} else if(weight != other.weight) {
return false;
} else if(radius != other.radius) {
return false;
} else if(pauseTimeMin != other.pauseTimeMin) {
return false;
} else if(pauseTimeMax != other.pauseTimeMax) {
return false;
}
return true; return true;
} }
@Override
public double getWeight() {
return weight;
}
@Override
public double getRadius() {
return radius;
}
@Override
public void setWeight(double weight) {
this.weight = weight;
}
@Override
public void setRadius(double radius) {
this.radius = radius;
if(radius > 0) {
this.area = Math.PI * Math.pow(radius, 2);
}
}
@Override
public long getPauseTimeMin() {
return pauseTimeMin;
}
@Override
public long getPauseTimeMax() {
return pauseTimeMax;
}
@Override
public void setPauseTime(long pauseTimeMin, long pauseTimeMax) {
assert pauseTimeMin >= 0 && pauseTimeMax >= 0;
assert pauseTimeMax >= pauseTimeMin;
this.pauseTimeMin = pauseTimeMin;
this.pauseTimeMax = pauseTimeMax;
}
@Override
public boolean hasPauseTime() {
if(pauseTimeMax >= 0 || pauseTimeMin >= 0) {
return true;
}
else
return false;
}
@Override
public boolean hasWeight() {
return true;
}
@Override
public boolean hasRadius() {
return true;
}
@Override
public String toString() {
return getName() + " (" + getX() + ", " + getY() + ")" + " <" + getRadius() + "> ";
}
public double getArea() {
return area;
}
} }
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import de.tudarmstadt.maki.simonstrator.api.component.core.MonitorComponent.Analyzer;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
public interface AttractionPointMonitor extends Analyzer {
public void addedAttractionPoint(IAttractionPoint ap);
public void removedAttractionPoint(IAttractionPoint ap);
}
/*
* Copyright (c) 2005-2015 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 <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.util.LinkedList;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import de.tud.kom.p2psim.api.common.SimHost;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
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.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
public class AttractionPointViz extends JComponent
implements VisualizationComponent {
protected boolean showAttractionPoints = true;
private JMenu menu;
private final static int ATTR_SIZE = 5;
private static Color COLOR_ATTR_POINT = Color.decode("#4A7B9D");
public static LinkedList<LinkedList<SimHost>> clusters = new LinkedList<LinkedList<SimHost>>();
public static LinkedList<Color> colors = new LinkedList<Color>();
public LinkedList<IAttractionPoint> aps;
public AttractionPointViz() {
init();
}
public void setAttractionPoints(LinkedList<IAttractionPoint> aps) {
this.aps = aps;
}
protected void init() {
setBounds(0, 0, VisualizationInjector.getWorldX(),
VisualizationInjector.getWorldY());
setOpaque(true);
setVisible(true);
menu = new JMenu("Attraction Points");
menu.add(createCheckboxAp());
}
private JCheckBoxMenuItem createCheckboxAp() {
final JCheckBoxMenuItem checkBox = new JCheckBoxMenuItem(
"show attraction points", showAttractionPoints);
checkBox.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
showAttractionPoints = checkBox.isSelected();
VisualizationInjector.invalidate();
}
});
return checkBox;
}
@Override
public void paint(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (showAttractionPoints) {
drawAttractionPoints(g2);
}
if(true) {
drawClusters(g2);
}
}
/**
* Provides access super.paintComponent() for extending classes.
* @param g the Graphics object for painting.
*/
protected void paintSuper(Graphics g)
{
super.paintComponent(g);
}
protected void drawClusters(Graphics2D g2)
{
// Composite gc = g2.getComposite();
if(colors.isEmpty()) {
for( int i = 0; i < 20; i++) {
colors.add(new Color(new java.util.Random().nextInt()));
}
}
for (LinkedList<SimHost> group : clusters) {
g2.setColor(colors.get(clusters.indexOf(group)));
for (SimHost member : group) {
PositionVector p = member.getTopologyComponent().getRealPosition();
g2.fillOval(VisualizationInjector.scaleValue(p.getX()) - 10,
VisualizationInjector.scaleValue(p.getY()) - 10, 20, 20);
}
}
}
/**
* Draws the attraction points. This method has been extracted from paint()
* to make it possible for extending class to override only this bit while leaving everything
* else untouched.
* @param g2 the Graphics2D object for painting.
*/
protected void drawAttractionPoints(Graphics2D g2)
{
if(aps == null || aps.size() == 0) {
return;
}
Composite gc = g2.getComposite();
for (IAttractionPoint aPoint : aps) {
Point point = ((PositionVector) aPoint).asPoint();
// draw border
g2.setColor(Color.BLACK);
g2.setFont(VisualizationTopologyView.FONT_MEDIUM);
g2.drawString(aPoint.getName(),
VisualizationInjector.scaleValue(point.x) - g2.getFontMetrics().stringWidth(aPoint.getName()) / 2,
VisualizationInjector.scaleValue(point.y - aPoint.getRadius() - 5) - ATTR_SIZE);
// g2.setColor(COLOR_ATTR_POINT);
// float alpha = 0.25f + (float) (aPoint.getWeight() / 2);
// g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
// g2.fillOval(
// VisualizationInjector.scaleValue(point.x) - ATTR_PAD,
// VisualizationInjector.scaleValue(point.y) - ATTR_PAD,
// ATTR_PAD * 2 + 1, ATTR_PAD * 2 + 1);
g2.setColor(COLOR_ATTR_POINT);
int radius = VisualizationInjector.scaleValue(aPoint.getRadius()) + ATTR_SIZE;
g2.drawOval(VisualizationInjector.scaleValue(point.x) - radius,
VisualizationInjector.scaleValue(point.y) - radius,
radius * 2 + 1, radius * 2 + 1);
g2.setComposite(gc);
}
}
@Override
public String getDisplayName() {
return "Attraction Points";
}
@Override
public JComponent getComponent() {
return this;
}
@Override
public JMenu getCustomMenu() {
return menu;
}
@Override
public boolean isHidden() {
return false;
}
}
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction; package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import de.tud.kom.p2psim.impl.topology.PositionVector; import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/** /**
* A basic attraction point, as simple as it can get. Really just a named location * A basic attraction point, as simple as it can get. Really just a named location
* without any checks performed. * without any checks performed.
*/ */
public class BasicAttractionPoint extends PositionVector implements AttractionPoint public class BasicAttractionPoint extends PositionVector implements IAttractionPoint
{ {
private String name; protected String name;
private double weight = 0;
private double radius = 0; public BasicAttractionPoint(String name, PositionVector pos) {
public BasicAttractionPoint(String name, PositionVector pos)
{
super(pos); super(pos);
this.name = name; this.name = name;
} }
@XMLConfigurableConstructor({ "name", "x", "y" })
public BasicAttractionPoint(String name, double x, double y) {
this(name, new PositionVector(x, y));
}
@Override @Override
public String getName() { public String getName() {
return name; return name;
} }
@Override
public double getWeight() {
return weight;
}
@Override
public double getRadius() {
return radius;
}
@Override
public void setWeight(double weight) {
this.weight = weight;
}
@Override @Override
public void setRadius(double radius) { public IAttractionPoint clone(String newName) {
this.radius = radius;
}
@Override
public AttractionPoint clone(String newName) {
return new BasicAttractionPoint(newName, this); return new BasicAttractionPoint(newName, this);
} }
} }
...@@ -20,28 +20,16 @@ ...@@ -20,28 +20,16 @@
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction; package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import java.util.LinkedList;
import java.util.List;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint;
/** /**
* For simple scenarios: add attraction points by specifying a coordinate. * For simple scenarios: add attraction points by specifying a coordinate.
* *
* @author Bjoern Richerzhagen * @author Bjoern Richerzhagen
* @version 1.0, Dec 11, 2015 * @version 1.0, Dec 11, 2015
*/ */
public class ConfigAttractionGenerator implements IAttractionGenerator { public class ConfigAttractionGenerator extends AbstractAttractionProvider {
private final List<AttractionPoint> points = new LinkedList<>();
@Override
public List<AttractionPoint> getAttractionPoints() {
return points;
}
public void setAttractionPoint(AttractionPoint point) { public void setAttractionPoint(AttractionPoint point) {
this.points.add(point); addAttractionPoint(point);
} }
} }
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
/**
* Implementation of the interface {@link AttractionGenerator}.
*
* @author Julian Zobel
* @version 1.0, April 2019
*/
public class ConfigDynamicAttractionGenerator extends AbstractAttractionProvider {
//private LinkedList<TemporalAttractionPoint> temporalAttractionPoints = new LinkedList<>();
public void setAttractionPoint(AttractionPoint ap) {
addAttractionPoint(ap);
}
public void setTemporalAttractionPoint(TemporalAttractionPoint ap) {
//temporalAttractionPoints.add(ap);
if(ap.getPlacementTime() == 0) {
placeAP(ap);
}
else {
Event.scheduleWithDelay(ap.getPlacementTime(), new EventHandler() {
@Override
public void eventOccurred(Object content, int type) {
placeAP(ap);
}
}, null, 0);
}
}
void placeAP(TemporalAttractionPoint ap) {
addAttractionPoint(ap);
Event.scheduleWithDelay(ap.getRemovalTime(), new EventHandler() {
@Override
public void eventOccurred(Object content, int type) {
removeAttractionPoint(ap);
}
}, null, 0);
}
}
...@@ -27,27 +27,25 @@ import java.util.LinkedList; ...@@ -27,27 +27,25 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import de.tud.kom.p2psim.api.topology.Topology; import de.tud.kom.p2psim.api.topology.Topology;
import de.tud.kom.p2psim.impl.topology.PositionVector; import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.Binder; import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor; import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/** /**
* Generates a given number of {@link AttractionPoint}s and sets the Position * Generates a given number of {@link IAttractionPoint}s and sets the Position
* according to a given CSV file. * according to a given CSV file.
* *
* @author Nils Richerzhagen * @author Nils Richerzhagen
* @version 1.0, 16.07.2014 * @version 1.0, 16.07.2014
*/ */
public class CsvAttractionGenerator implements IAttractionGenerator { public class CsvAttractionGenerator extends AbstractAttractionProvider {
private PositionVector worldDimensions;
private String file; private String file;
private final String SEP = ";"; private final String SEP = ";";
private List<AttractionPoint> attractionPoints; private double radius = 0;
/** /**
* *
...@@ -55,18 +53,22 @@ public class CsvAttractionGenerator implements IAttractionGenerator { ...@@ -55,18 +53,22 @@ public class CsvAttractionGenerator implements IAttractionGenerator {
*/ */
@XMLConfigurableConstructor({ "placementFile" }) @XMLConfigurableConstructor({ "placementFile" })
public CsvAttractionGenerator(String placementFile) { public CsvAttractionGenerator(String placementFile) {
this.worldDimensions = Binder.getComponentOrNull(Topology.class) super();
.getWorldDimensions();
this.file = placementFile; this.file = placementFile;
readData();
} }
public void setRadius(double radius) {
this.radius = radius;
}
@Override @Override
public List<AttractionPoint> getAttractionPoints() { public LinkedList<AttractionPoint> getAttractionPoints() {
if (attractionPoints == null) { if (super.getAttractionPoints().isEmpty()) {
attractionPoints = new LinkedList<>();
readData(); readData();
} }
return attractionPoints; return super.getAttractionPoints();
} }
private void readData() { private void readData() {
...@@ -86,15 +88,44 @@ public class CsvAttractionGenerator implements IAttractionGenerator { ...@@ -86,15 +88,44 @@ public class CsvAttractionGenerator implements IAttractionGenerator {
Double x = Double.parseDouble(parts[0]); Double x = Double.parseDouble(parts[0]);
Double y = Double.parseDouble(parts[1]); Double y = Double.parseDouble(parts[1]);
if (x > worldDimensions.getX() if (x > worldDimension.getX()
|| y > worldDimensions.getY() || x < 0 || y > worldDimension.getY() || x < 0
|| y < 0) {
System.err.println("Skipped entry " + x + ";"
+ y);
continue;
}
AttractionPoint ap = new AttractionPoint("AP"+i, new PositionVector(x, y));
ap.setRadius(radius);
addAttractionPoint(ap);
i++;
entrySuccessfullyRead = true;
} catch (NumberFormatException e) {
// Ignore leading comments
if (entrySuccessfullyRead) {
// System.err.println("CSV ParseError " + line);
}
}
}
else if(parts.length == 3) {
try {
Double x = Double.parseDouble(parts[0]);
Double y = Double.parseDouble(parts[1]);
Double r = Double.parseDouble(parts[2]);
if (x > worldDimension.getX()
|| y > worldDimension.getY() || x < 0
|| y < 0) { || y < 0) {
System.err.println("Skipped entry " + x + ";" System.err.println("Skipped entry " + x + ";"
+ y); + y);
continue; continue;
} }
attractionPoints.add(new AttractionPointImpl("AP"+i, new PositionVector(x,
y))); AttractionPoint ap = new AttractionPoint("AP"+i, new PositionVector(x, y));
ap.setRadius(r);
addAttractionPoint(ap);
i++; i++;
entrySuccessfullyRead = true; entrySuccessfullyRead = true;
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
...@@ -104,6 +135,7 @@ public class CsvAttractionGenerator implements IAttractionGenerator { ...@@ -104,6 +135,7 @@ public class CsvAttractionGenerator implements IAttractionGenerator {
} }
} }
} }
} else { } else {
throw new AssertionError("To many columns in CSV."); throw new AssertionError("To many columns in CSV.");
} }
......
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import java.util.LinkedList;
import java.util.List;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* Attraction Generator, providing 4 equidistant attraction points, i.e., in a square
*
* @author Julian Zobel
* @version 1.0, Nov 2018
*/
public class EquidistantSquareAttractionGenerator extends AbstractAttractionProvider {
private PositionVector worldDimension;
private double squareSize;
@XMLConfigurableConstructor({ "squareSize" })
public EquidistantSquareAttractionGenerator(double squareSize) {
this.squareSize = squareSize;
createAttractionPoints();
}
@Override
public LinkedList<AttractionPoint> getAttractionPoints() {
if(super.getAttractionPoints().isEmpty())
createAttractionPoints();
return super.getAttractionPoints();
}
private void createAttractionPoints() {
double x = worldDimension.getX() / 2;
double y = worldDimension.getY() / 2;
PositionVector p1 = new PositionVector(x - squareSize, y - squareSize);
this.addAttractionPoint(new AttractionPoint("AP1", p1));
PositionVector p2 = new PositionVector(x - squareSize, y + squareSize);
this.addAttractionPoint(new AttractionPoint("AP2", p2));
PositionVector p3 = new PositionVector(x + squareSize, y - squareSize);
this.addAttractionPoint(new AttractionPoint("AP3", p3));
PositionVector p4 = new PositionVector(x + squareSize, y + squareSize);
this.addAttractionPoint(new AttractionPoint("AP4", p4));
}
}
...@@ -21,19 +21,16 @@ ...@@ -21,19 +21,16 @@
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction; package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import java.util.List; import java.util.List;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint;
/** /**
* This is the interface for the generator of the {@link AttractionPoint}s. It * Interface for {@link IAttractionPoint} generators. Will generate a given amount of attraction points.
* gets the set number of AttractionPoints back. This mean, it will be generate
* them and set the Position of them.
* *
* @author Christoph Muenker * @author Christoph Muenker, Julian Zobel
* @version 1.0, 02.07.2013 * @version 1.1, 09 2018
*/ */
public interface IAttractionGenerator { public interface IAttractionProvider {
public List<AttractionPoint> getAttractionPoints(); public List<AttractionPoint> getAttractionPoints();
} }
\ No newline at end of file
...@@ -25,17 +25,12 @@ import java.io.FileNotFoundException; ...@@ -25,17 +25,12 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import de.tud.kom.p2psim.api.topology.Topology;
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.movement.modularosm.GPSCalculation;
import de.tudarmstadt.maki.simonstrator.api.Binder; import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint;
/** /**
* Generates attraction points out of real data from osm * Generates attraction points out of real data from osm
...@@ -43,94 +38,128 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Attraction ...@@ -43,94 +38,128 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Attraction
* *
* @author Martin Hellwig * @author Martin Hellwig
* @version 1.0, 02.07.2015 * @version 1.0, 02.07.2015
*
* Added an upper limit for the number of attraction points. Use 0 to allow all APs in the JSON file.
* @author Julian Zobel
* @version 1.1, November 2018
*
* You now can set an upper limit for the radius.
* @author Julian Zobel
* @version 1.2, January 2019
*
*
*/ */
public class JSONAttractionGenerator implements IAttractionGenerator { public class JSONAttractionGenerator extends AbstractAttractionProvider {
private PositionVector worldDimensions; protected int numberOfAttractionPoints;
protected String placementJsonFile = "";
private List<AttractionPoint> attractionPoints; protected double maximumRadius = -1; // Values >= 0, or -1 if radius taken from file
private String placementJsonFile;
private double latLeft; //Values from -90 to 90; always smaller than latRight
private double latRight; //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
/** /**
* You have to set a json-file, which has set some POIs * You have to set a json-file, which has set some POIs
* Sample-query for "bar"-POIs in Darmstadt (Bounding Box from [49.4813, 8.5590] to [49.9088, 8,7736]: * Sample-query for "bar"-POIs in Darmstadt (Bounding Box from [49.4813, 8.5590] to [49.9088, 8,7736]:
http://overpass-api.de/api/interpreter?data=%5Bout:json%5D;node%5Bamenity=bar%5D%2849%2E4813%2C8%2E5590%2C49%2E9088%2C8%2E7736%29%3Bout%3B http://overpass-api.de/api/interpreter?data=%5Bout:json%5D;node%5Bamenity=bar%5D%2849%2E4813%2C8%2E5590%2C49%2E9088%2C8%2E7736%29%3Bout%3B
*/ */
public JSONAttractionGenerator() { @XMLConfigurableConstructor({"numberOfAttractionPoints", "maximumRadius", "placementJsonFile"})
this.worldDimensions = Binder.getComponentOrNull(Topology.class) public JSONAttractionGenerator(int numberOfAttractionPoints, double maximumRadius, String placementJsonFile) {
.getWorldDimensions(); this.numberOfAttractionPoints = numberOfAttractionPoints;
attractionPoints = new LinkedList<AttractionPoint>(); this.maximumRadius = maximumRadius;
this.placementJsonFile = placementJsonFile;
latLeft = GPSCalculation.getLatLower(); createAttractionPoints();
latRight = GPSCalculation.getLatUpper();
lonLeft = GPSCalculation.getLonLeft();
lonRight = GPSCalculation.getLonRight();
}
/**
* 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);
//Invert the y value, because in Java Swing we start drawing in the upper left corner instead in the lower left one
double y = worldDimensions.getY() - worldDimensions.getY() * (lat - latLeft)/(latRight - latLeft);
return new PositionVector(x, y);
} }
@Override @Override
public List<AttractionPoint> getAttractionPoints() { public LinkedList<AttractionPoint> getAttractionPoints() {
if(attractionPoints.size() == 0) { if(super.getAttractionPoints().size() == 0) {
String poiString = ""; createAttractionPoints();
JSONArray allPOI = null; }
FileInputStream inputStream;
try { return super.getAttractionPoints();
inputStream = new FileInputStream(placementJsonFile); }
poiString = IOUtils.toString(inputStream);
JSONObject poiData = new JSONObject(poiString); protected JSONArray getPOIArray() {
allPOI = poiData.getJSONArray("elements"); assert !placementJsonFile.equals("");
} catch (FileNotFoundException e) {
e.printStackTrace(); String poiString = "";
} catch (IOException e) { JSONArray allPOI = null;
e.printStackTrace(); FileInputStream inputStream;
} catch (JSONException e) { try {
e.printStackTrace(); inputStream = new FileInputStream(placementJsonFile);
} poiString = IOUtils.toString(inputStream);
JSONObject poiData = new JSONObject(poiString);
allPOI = poiData.getJSONArray("elements");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return allPOI;
}
private void createAttractionPoints() {
assert super.getAttractionPoints().size() == 0;
JSONArray allPOI = getPOIArray();
if(allPOI == null) {
throw new UnsupportedOperationException("[JSONAttractionGenerator] POI Data Array cannot be NULL.");
}
if(allPOI != null) { int limit = 0;
for(int i = 0; i < allPOI.length(); i++) { if(numberOfAttractionPoints == 0 || numberOfAttractionPoints > allPOI.length()) {
try { limit = allPOI.length();
String barname = allPOI.getJSONObject(i).getJSONObject("tags").getString("name"); }
double lat = allPOI.getJSONObject(i).getDouble("lat"); else {
double lon = allPOI.getJSONObject(i).getDouble("lon"); limit = numberOfAttractionPoints;
AttractionPointImpl ap; }
if(lat > latLeft && lat < latRight &&
lon > lonLeft && lon < lonRight) { for(int i = 0; i < limit; i++) {
ap = new AttractionPointImpl(barname, transformGPSWindowToOwnWorld(lat, lon)); try {
attractionPoints.add(ap); String barname = allPOI.getJSONObject(i).getJSONObject("tags").getString("name");
// the following is allowed to fail. double lat = allPOI.getJSONObject(i).getDouble("lat");
ap.setWeight(allPOI.getJSONObject(i).getJSONObject("tags").getDouble("weight")); double lon = allPOI.getJSONObject(i).getDouble("lon");
ap.setRadius(allPOI.getJSONObject(i).getJSONObject("tags").getDouble("radius")); AttractionPoint ap;
}
// check that the point is within the simulation boundaries
if(GPSCalculation.isWithinGPSBoundaries(lat, lon)) {
// initialize the attraction point with basic information, will be filled now...
ap = new AttractionPoint(barname, GPSCalculation.transformGPSWindowToOwnWorld(lat, lon));
// the following setters are allowed to fail
// AP weight
if(allPOI.getJSONObject(i).getJSONObject("tags").has("weight")) {
ap.setWeight(allPOI.getJSONObject(i).getJSONObject("tags").getDouble("weight"));
} }
catch (JSONException e) {
//This bar had no name defined, so there was an error. Not so bad // AP radius
if(allPOI.getJSONObject(i).getJSONObject("tags").has("radius")) {
double radius = allPOI.getJSONObject(i).getJSONObject("tags").getDouble("radius");
if(maximumRadius == -1) {
ap.setRadius(radius);
}
else {
ap.setRadius(Math.min(maximumRadius, radius));
}
} }
if(allPOI.getJSONObject(i).getJSONObject("tags").has("pauseTimeMin") && allPOI.getJSONObject(i).getJSONObject("tags").has("pauseTimeMax")) {
ap.setPauseTime( allPOI.getJSONObject(i).getJSONObject("tags").getLong("pauseTimeMin"), allPOI.getJSONObject(i).getJSONObject("tags").getLong("pauseTimeMax"));
}
addAttractionPoint(ap);
} }
} }
} catch (JSONException e) {
//This bar had no name defined, so there was an error. Not so bad
return attractionPoints; System.out.println(e);
} }
}
public void setPlacementJsonFile(String placementJsonFile) { }
this.placementJsonFile = placementJsonFile;
}
} }
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import java.util.LinkedList;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
public class MobileAttractionPoint extends AttractionPoint {
private LinkedList<PositionVector> waypoints;
private double speed = 0.5;
private boolean move = false;
private long pausetime = Time.MINUTE * 10;
private long timer = 0L;
@XMLConfigurableConstructor({ "name", "x", "y" })
public MobileAttractionPoint(String name, double x, double y) {
super(name, x, y);
waypoints = new LinkedList<PositionVector>();
waypoints.add(new PositionVector(250,250));
waypoints.add(new PositionVector(250,400));
waypoints.add(new PositionVector(750,400));
waypoints.add(new PositionVector(750,250));
}
public void move() {
if(move) {
PositionVector destination = waypoints.getFirst();
if (destination.distanceTo(this) > speed) {
this.set(this.moveStep(destination, speed));
}
else {
this.set(destination);
waypoints.add(waypoints.removeFirst());
timer = 0;
move = false;
}
}
else {
timer += Time.SECOND;
if(timer >= pausetime) {
timer = 0;
move = true;
}
}
}
}
...@@ -21,26 +21,18 @@ ...@@ -21,26 +21,18 @@
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction; package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import de.tud.kom.p2psim.api.topology.Topology;
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.movement.modularosm.GPSCalculation;
import de.tudarmstadt.maki.simonstrator.api.Binder; import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint;
/** /**
* Generates attraction points out of real data from osm * Generates attraction points out of real data from osm
...@@ -49,150 +41,58 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Attraction ...@@ -49,150 +41,58 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Attraction
* @author Martin Hellwig * @author Martin Hellwig
* @version 1.0, 02.07.2015 * @version 1.0, 02.07.2015
*/ */
public class OnlineJSONAttractionGenerator implements IAttractionGenerator { public class OnlineJSONAttractionGenerator extends JSONAttractionGenerator {
private PositionVector worldDimensions;
private List<AttractionPoint> attractionPoints;
private int maxNumberOfAttractionPoints;
private String placementJsonFile;
private String placementJsonPath;
private String amenity; private String amenity;
private double latLeft; //Values from -90 to 90; always smaller than latRight
private double latRight; //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
public OnlineJSONAttractionGenerator() {
this.worldDimensions = Binder.getComponentOrNull(Topology.class)
.getWorldDimensions();
attractionPoints = new LinkedList<AttractionPoint>();
latLeft = GPSCalculation.getLatLower(); @XMLConfigurableConstructor({"numberOfAttractionPoints", "maximumRadius", "placementJsonPath", "amenity"})
latRight = GPSCalculation.getLatUpper(); public OnlineJSONAttractionGenerator(int numberOfAttractionPoints, double maximumRadius,
lonLeft = GPSCalculation.getLonLeft(); String placementJsonPath, String amenity) {
lonRight = GPSCalculation.getLonRight();
}
/** super(numberOfAttractionPoints, maximumRadius, placementJsonPath +
* Projects the gps coordinates in the given gps window to the world-coordinates given in world-dimensions "pois" +
* @param lat GPSCalculation.getLatCenter() +
* @param lon GPSCalculation.getLonCenter() +
* @return The projected position in world-dimensions GPSCalculation.getZoom() +
*/ amenity + ".json");
private PositionVector transformGPSWindowToOwnWorld(double lat, double lon) {
double x = worldDimensions.getX() * (lon - lonLeft)/(lonRight - lonLeft); this.amenity = amenity;
//Invert the y value, because in Java Swing we start drawing in the upper left corner instead in the lower left one }
double y = worldDimensions.getY() - worldDimensions.getY() * (lat - latLeft)/(latRight - latLeft);
return new PositionVector(x, y);
}
public void setNumberOfAttractionPoints(int numberOfAttractionPoints) {
this.maxNumberOfAttractionPoints = numberOfAttractionPoints;
}
@Override @Override
public List<AttractionPoint> getAttractionPoints() { protected JSONArray getPOIArray() {
if(attractionPoints.size() == 0) { //Check if the file with same properties (same location) already exists
placementJsonFile = placementJsonPath + File f = new File(placementJsonFile);
"pois" + if(!f.exists()) {
GPSCalculation.getLatCenter() + String poiString = "";
GPSCalculation.getLonCenter() + JSONArray allPOI = null;
GPSCalculation.getZoom() + InputStream in;
amenity + ".json"; try {
in = new URL( "http://overpass-api.de/api/interpreter?data=%5Bout:json%5D;node%5Bamenity=" + amenity + "%5D%28"
//Check if the file with same properties (same location) already exists + GPSCalculation.getLatUpper() + "%2C" + GPSCalculation.getLonLeft() + "%2C"
File f = new File(placementJsonFile); + GPSCalculation.getLatLower() + "%2C" + GPSCalculation.getLonRight() + "%29%3Bout%3B" ).openStream();
if(!f.exists()) { poiString = IOUtils.toString(in);
String poiString = ""; //Save the json data in file
JSONArray allPOI = null; PrintWriter out = new PrintWriter(placementJsonFile);
InputStream in; out.print(poiString);
try { out.close();
in = new URL( "http://overpass-api.de/api/interpreter?data=%5Bout:json%5D;node%5Bamenity=" + amenity + "%5D%28" + latLeft + "%2C" + lonLeft + "%2C" + latRight + "%2C" + lonRight + "%29%3Bout%3B" ).openStream(); JSONObject poiData = new JSONObject(poiString);
poiString = IOUtils.toString(in); allPOI = poiData.getJSONArray("elements");
//Save the json data in file } catch (JSONException e) {
PrintWriter out = new PrintWriter(placementJsonFile); e.printStackTrace();
out.print(poiString); } catch (MalformedURLException e) {
out.close(); // TODO Auto-generated catch block
JSONObject poiData = new JSONObject(poiString); e.printStackTrace();
allPOI = poiData.getJSONArray("elements"); } catch (IOException e) {
} catch (JSONException e) { // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(allPOI != null) {
for(int i = 0; i < allPOI.length(); i++) {
try {
String barname = allPOI.getJSONObject(i).getJSONObject("tags").getString("name");
double lat = allPOI.getJSONObject(i).getDouble("lat");
double lon = allPOI.getJSONObject(i).getDouble("lon");
if(lat > latLeft && lat < latRight &&
lon > lonLeft && lon < lonRight) {
attractionPoints.add(new AttractionPointImpl(barname, transformGPSWindowToOwnWorld(lat, lon)));
}
}
catch (JSONException e) {
//This bar had no name defined, so there was an error. Not so bad
}
}
}
} }
else {
//File already exists, now we have to parse this file return allPOI;
String poiString = "";
JSONArray allPOI = null;
FileInputStream inputStream;
try {
inputStream = new FileInputStream(placementJsonFile);
poiString = IOUtils.toString(inputStream);
JSONObject poiData = new JSONObject(poiString);
allPOI = poiData.getJSONArray("elements");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
if(allPOI != null) {
for(int i = 0; i < allPOI.length(); i++) {
try {
String barname = allPOI.getJSONObject(i).getJSONObject("tags").getString("name");
double lat = allPOI.getJSONObject(i).getDouble("lat");
double lon = allPOI.getJSONObject(i).getDouble("lon");
attractionPoints.add(new AttractionPointImpl(barname, transformGPSWindowToOwnWorld(lat, lon)));
}
catch (JSONException e) {
//This bar had no name defined, so there was an error. Not so bad
}
}
}
}
}
if(maxNumberOfAttractionPoints == 0) maxNumberOfAttractionPoints = Integer.MAX_VALUE;
List<AttractionPoint> result = new LinkedList<AttractionPoint>();
for (int i = 0; (i < attractionPoints.size() && i < maxNumberOfAttractionPoints); i++) {
result.add(attractionPoints.get(i));
} }
return result; else {
} return super.getPOIArray();
}
public void setAmenity(String amenity) {
this.amenity = amenity;
}
public void setPlacementJsonPath(String placementJsonPath) {
this.placementJsonPath = placementJsonPath;
} }
} }
...@@ -20,64 +20,121 @@ ...@@ -20,64 +20,121 @@
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction; package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.Vector;
import de.tud.kom.p2psim.api.scenario.ConfigurationException; import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.api.topology.Topology; import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.Randoms; import de.tudarmstadt.maki.simonstrator.api.Randoms;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor; import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/** /**
* Implementation of the interface {@link AttractionGenerator}. * Implementation of the interface {@link AttractionGenerator}.
* *
* It generates the given number of {@link AttractionPoint}s and sets the * Generates the given number of {@link IAttractionPoint}s and sets the
* Position randomly. * position randomly within the world dimensions.
*
* v1.1: statically available attraction points [JZ]
* v1.2: added a minimum distance between attraction points [JZ]
* v1.21: added a random radius to attraction points. Minimum is 10 meters, default maximum 100 meters. APs cannot have overlapping radii.
* *
* @author Christoph Muenker * @author Christoph Muenker, Julian Zobel
* @version 1.0, 02.07.2013 * @version 1.21, 12 2018
*/ */
public class RandomAttractionGenerator implements IAttractionGenerator { public class RandomAttractionGenerator extends AbstractAttractionProvider {
private Random rand; private Random rand;
private PositionVector worldDimension;
private int numberOfAttractionPoints; private int numberOfAttractionPoints;
private boolean numberOfAPsSet = false; private boolean numberOfAPsSet = false;
@XMLConfigurableConstructor({"numberOfAttractionPoints"}) private double minimumDistance = 50;
public RandomAttractionGenerator(int numberOfAttractionPoints) {
this.rand = Randoms.getRandom(RandomAttractionGenerator.class); private double maximumRadius = 100;
this.worldDimension = Binder.getComponentOrNull(Topology.class)
.getWorldDimensions(); private double minimumRadius = 10;
@XMLConfigurableConstructor({"numberOfAttractionPoints", "maximumRadius", "minimumRadius", "minimumDistance"})
public RandomAttractionGenerator(int numberOfAttractionPoints, double maximumRadius, double minimumRadius, double minimumDistance) {
super();
this.rand = Randoms.getRandom(RandomAttractionGenerator.class);
if (numberOfAttractionPoints <= 0) { if (numberOfAttractionPoints <= 0) {
throw new ConfigurationException( throw new ConfigurationException(
"NumberOfAttractionPoints should be at least 1!"); "NumberOfAttractionPoints should be at least 1!");
} }
this.numberOfAPsSet = true; this.numberOfAPsSet = true;
this.numberOfAttractionPoints = numberOfAttractionPoints; this.numberOfAttractionPoints = numberOfAttractionPoints;
this.maximumRadius = maximumRadius;
this.minimumRadius = minimumRadius;
this.minimumDistance = minimumDistance;
createAttractionPoints();
} }
@Override @Override
public List<AttractionPoint> getAttractionPoints() { public LinkedList<AttractionPoint> getAttractionPoints() {
if (!numberOfAPsSet) { if (!numberOfAPsSet) {
throw new ConfigurationException( throw new ConfigurationException(
"Number of Attraction Points is not set in RandomAttractionGenerator!"); "Number of Attraction Points is not set in RandomAttractionGenerator!");
} }
List<AttractionPoint> result = new Vector<AttractionPoint>(); if(super.getAttractionPoints().isEmpty())
for (int i = 0; i < numberOfAttractionPoints; i++) { createAttractionPoints();
return super.getAttractionPoints();
}
private void createAttractionPoints() {
LinkedList<AttractionPoint> result = new LinkedList<AttractionPoint>();
// make a break counter to prevent more than 10 iterations and an infinity loop in general.
int c = 0;
create: for (int i = 0; i < numberOfAttractionPoints; i++) {
PositionVector posVec = createPosVec(); PositionVector posVec = createPosVec();
AttractionPoint aPoint = new AttractionPointImpl("AP"+i,posVec);
// set the radius of this attraction point
// minimum radius is 10 meters
double radius = Math.max(minimumRadius, rand.nextDouble() * maximumRadius);
if(c < 20)
{
// if not within the world dimensions, directly go back to calculation
if(posVec.getX() + radius > worldDimension.getX() || posVec.getY() + radius > worldDimension.getY()
|| posVec.getX() - radius < 0 || posVec.getY() - radius < 0) {
i--;
c++;
continue create;
}
for (IAttractionPoint ap : result) {
// if this point is closer than the given minimum distance to another point, or the radii of the points would overlap,
// or if the radius would exceed the simulation area
// then discard this attraction point and create a new one
if(posVec.distanceTo(ap) < minimumDistance || (posVec.distanceTo(ap) - radius - ap.getRadius()) < 0 ) {
i--;
c++;
continue create;
}
}
}
else
{
radius = minimumRadius;
}
AttractionPoint aPoint = new AttractionPoint("AP-"+i, posVec);
aPoint.setRadius(radius);
c = 0;
result.add(aPoint); result.add(aPoint);
} }
return result;
for (AttractionPoint ap : result) {
addAttractionPoint(ap);
}
} }
private PositionVector createPosVec() { private PositionVector createPosVec() {
...@@ -85,5 +142,34 @@ public class RandomAttractionGenerator implements IAttractionGenerator { ...@@ -85,5 +142,34 @@ public class RandomAttractionGenerator implements IAttractionGenerator {
double y = rand.nextDouble() * worldDimension.getY(); double y = rand.nextDouble() * worldDimension.getY();
return new PositionVector(x, y); return new PositionVector(x, y);
} }
/**
* Set a minimum distance between the randomly generated attraction points
* @param distance
*/
public void setMinimumDistance(double distance) {
this.minimumDistance = distance;
}
/**
* Set a maximum radius that an attraction point can have.
* @param radius
*/
public void setMaximumRadius(double radius) {
this.maximumRadius = radius;
}
/**
* Set a minimum radius that an attraction point can have. Minimum is 10 meters.
* @param radius
*/
public void setMinimumRadius(double radius) {
if(radius < 10) {
this.minimumRadius = 10;
}
else {
this.minimumRadius = radius;
}
}
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment