/*
* File name: CarsEnvironment.java (package eas.users.ocScenario.cars)
* Author(s): Lukas König
* Java version: 7.0
* Generation date: 09.05.2014 (14:54:07)
*
* (c) This file and the EAS (Easy Agent Simulation) framework containing it
* is protected by Creative Commons by-nc-sa license. Any altered or
* further developed versions of this file have to meet the agreements
* stated by the license conditions.
*
* In a nutshell
* -------------
* You are free:
* - to Share -- to copy, distribute and transmit the work
* - to Remix -- to adapt the work
*
* Under the following conditions:
* - Attribution -- You must attribute the work in the manner specified by the
* author or licensor (but not in any way that suggests that they endorse
* you or your use of the work).
* - Noncommercial -- You may not use this work for commercial purposes.
* - Share Alike -- If you alter, transform, or build upon this work, you may
* distribute the resulting work only under the same or a similar license to
* this one.
*
* + Detailed license conditions (Germany):
* http://creativecommons.org/licenses/by-nc-sa/3.0/de/
* + Detailed license conditions (unported):
* http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en
*
* This header must be placed in the beginning of any version of this file.
*/
package eas.users.ocScenarios.traffic;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import eas.math.geometry.Polygon2D;
import eas.math.geometry.Vector2D;
import eas.plugins.standard.liveInteraction.MethodRunnableProperties;
import eas.plugins.standard.visualization.AllroundVideoPlugin;
import eas.simulation.Wink;
import eas.simulation.spatial.sim2D.gridSimulation.standardEnvironments.AbstractGridEnvironment;
import eas.simulation.spatial.sim2D.gridSimulation.standardGridObjects.GridObject;
import eas.simulation.spatial.sim2D.standardAgents.AbstractAgent2D;
import eas.startSetup.ParCollection;
import eas.startSetup.SingleParameter;
import eas.users.ocScenarios.traffic.lights.LightsPlugin;
import eas.users.ocScenarios.traffic.strategies.StrategyFactory;
/**
* @author Lukas König
*/
public class TrafficEnvironment extends AbstractGridEnvironment<AbstractAgent2D<?>> {
/**
* All serializable classes must have this.
*/
private static final long serialVersionUID = 5640257554252878857L;
/**
* A globally used (within cars scenario) random number generator.
*/
public Random RAND;
/**
* Standard constructor.
*
* @param id The environments id (pretty much arbitrary).
* @param params The program parameters.
*/
public TrafficEnvironment(int id, ParCollection params) {
super(id, params, params.getParValueInt("GridFields"), params.getParValueInt("GridFields"), true);
RAND = new Random(params.getSeed());
}
@Override
@MethodRunnableProperties(ignoreMethod=true)
public List<SingleParameter> getParameters() {
List<SingleParameter> parsList = super.getParameters();
parsList.addAll(TrafficParameters.getParameter());
return parsList;
}
/**
* Performs areset procedure including a newly triggered positioning of
* streets, cars etc.
*/
@MethodRunnableProperties(ignoreMethod=true)
private void reset() {
this.removeAllAgents();
this.removeAllGridObjects();
try {
this.positionStreets(TrafficParameters.numStreets, TrafficParameters.proportionOfRoundabouts / 100.0);
this.positionCars(TrafficParameters.numCars);
AllroundVideoPlugin vid = (AllroundVideoPlugin) this.getSimTime().getPluginObject(new AllroundVideoPlugin().id());
vid.showTooltipps(false);
vid.setFollowAgent(true);
} catch (Exception e) {}
}
/**
* Positions the specified number of cars randomly on the streets, if
* possible. If not, as many cars are positioned as possible.
*
* @param numAgents The desired number of cars to position.
*/
public void positionCars(int numAgents) {
ArrayList<AbstractAgent2D<?>> list = new ArrayList<>(numAgents);
for (int i = 0; i < numAgents; i++) {
CarAgent ca = new CarAgent(
i + 1000,
this,
this.getParCollection(),
StrategyFactory.getDrivingStrategy(TrafficParameters.drivingStrategy),
StrategyFactory.getCollisionStrategy(TrafficParameters.collisionStrategy));
list.add(ca);
}
this.distributeAgentsOverEmptyFields(list, l -> l.size() == 1);
}
@MethodRunnableProperties(ignoreMethod=true)
private boolean isInCircleArea(int val, int size) {
if (TrafficParameters.roundaboutRadius * 2 >= size) {
return false;
}
return val % size - size / 2 > 0 && val % size - size / 2 <= TrafficParameters.roundaboutRadius
|| val % size - size / 2 - 1 < 0 && val % size - size / 2 - 1 >= -TrafficParameters.roundaboutRadius;
}
@MethodRunnableProperties(ignoreMethod=true)
private boolean isOnCircle(int val, int size) {
if (TrafficParameters.roundaboutRadius * 2 >= size) {
return false;
}
return val % size - size / 2 == TrafficParameters.roundaboutRadius || val % size - size / 2 - 1 == -TrafficParameters.roundaboutRadius;
}
@MethodRunnableProperties(ignoreMethod=true)
private boolean isInCircleArea(int x, int y, int size) {
return isInCircleArea(x, size) && isInCircleArea(y, size);
}
@MethodRunnableProperties(ignoreMethod=true)
private boolean isOnCircle(int x, int y, int size) {
return isOnCircle(x, size) && isInCircleArea(y, size)
|| isOnCircle(y, size) && isInCircleArea(x, size);
}
private HashMap<Vector2D, Boolean> circles = new HashMap<>();
@MethodRunnableProperties(ignoreMethod=true)
public HashMap<Vector2D, Boolean> getCirclesDist() {
return this.circles;
}
@MethodRunnableProperties(ignoreMethod=true)
public void positionStreets(int noCrossings, double circleProb) {
int dir = this.getWidth() / (noCrossings) + 1;
int size = dir - 1;
int crossingsX = 0;
int crossingsY = 0;
circles = new HashMap<>();
Boolean nextIsCircle = false;
for (int i = 0; i < this.getWidth(); i++) {
if (i % size == 0) {
crossingsX++;
}
crossingsY = 0;
for (int j = 0; j < this.getHeight(); j++) {
if (j % size == 0) {
crossingsY++;
}
Vector2D vec = new Vector2D(crossingsX, crossingsY);
nextIsCircle = circles.get(vec);
if (nextIsCircle == null) {
circles.put(vec, RAND.nextDouble() < circleProb);
nextIsCircle = circles.get(vec);
}
if (isOnCircle(i, j, size) && nextIsCircle) {
StreetObject agent = new StreetObject();
agent.setOnCircle(true);
if (i % size <= size / 2) {
agent.setRightLaneNtoS(true);
}
if (j % size <= size / 2) {
agent.setRightLaneWtoE(true);
}
if (i % size >= size / 2 + 1) {
agent.setLeftLaneNtoS(true);
}
if (j % size >= size / 2 + 1) {
agent.setLeftLaneWtoE(true);
}
this.addGridObject(agent, i, j);
} else if (!nextIsCircle || !isInCircleArea(i, j, size)) {
if (i % size == size / 2 || j % size == size / 2
|| (TrafficParameters.twoLanes && (i % size == size / 2 + 1 || j % size == size / 2 + 1))) {
StreetObject agent = new StreetObject();
if (i % size == size / 2) {
agent.setRightLaneNtoS(true);
}
if (j % size == size / 2) {
agent.setRightLaneWtoE(true);
}
if (TrafficParameters.twoLanes) {
if (i % size == size / 2 + 1) {
agent.setLeftLaneNtoS(true);
}
if (j % size == size / 2 + 1) {
agent.setLeftLaneWtoE(true);
}
}
this.addGridObject(agent, i, j);
}
}
}
}
}
private int waitingTime;
public int getNumOfWaitingCars() {
return waitingTime;
}
public void setWaitingTime(int waitingTime) {
this.waitingTime = waitingTime;
}
public int getNumOfStoredTracePoints() {
return TrafficParameters.numOfStoredTracePoints;
}
@Override
public void step(Wink simTime) {
super.step(simTime);
if (TrafficParameters.resetRequested) {
this.reset();
try {
LightsPlugin plug = (LightsPlugin) this.getSimTime().getPluginObject(new LightsPlugin().id());
plug.reset(this, this.getParCollection());
} catch (Exception e) {}
// ChartEvent e2 = new ChartEvent("Cars", "Avg. speed", 0);
// e2.setChartFramePos(new Vector2D(0, 300));
// this.getSimTime().broadcastEvent(e2);
TrafficParameters.resetRequested = false;
} else {
// ChartEvent e1 = new ChartEvent("Cars", "Waiting time", (this.getWaitingTime()));
// ChartEvent e2 = new ChartEvent("Cars", "Avg. speed", this.getAverageSpeed() * 100);
// e1.setyAxisLabel("Waiting cars per tick");
// this.getSimTime().broadcastEvent(e1);
// this.getSimTime().broadcastEvent(e2);
}
this.waitingTime = 0;
}
public double getAverageSpeed() {
float speed = 0;
List<CarAgent> carList = this.getAllCars();
for (AbstractAgent2D<?> c : carList) {
speed += ((CarAgent) c).getSpeedD();
}
return speed / carList.size();
}
@MethodRunnableProperties(ignoreMethod=true)
public ArrayList<CarAgent> getAllCars() {
ArrayList<CarAgent> cars = new ArrayList<>(this.getAgents().size());
for (AbstractAgent2D<?> a : this.getAgents()) {
if (a.getClass().equals(CarAgent.class)) {
cars.add((CarAgent) a);
}
}
return cars;
}
@MethodRunnableProperties(ignoreMethod=true)
public HashMap<Vector2D, StreetObject> getAllStreetAgents() {
HashMap<Vector2D, StreetObject> streetAgents = new HashMap<Vector2D, StreetObject>();
for (int i = 0; i < this.getGridWidth(); i++) {
for (int j = 0; j < this.getGridHeight(); j++) {
for (GridObject a : this.getFieldPosition(new Vector2D(i, j))) {
if (a.getClass().equals(StreetObject.class)) {
streetAgents.put(new Vector2D(i, j), (StreetObject) a);
}
}
}
}
return streetAgents;
}
@MethodRunnableProperties(ignoreMethod=true)
public boolean isRightLaneNtoS(int column, int row) {
List<GridObject> field = this.getFieldPosition(column, row);
for (GridObject g : field) {
if (g.getClass().equals(StreetObject.class)) {
return ((StreetObject) g).isRightLaneNtoS();
}
}
return false;
}
@MethodRunnableProperties(ignoreMethod=true)
public boolean isLeftLaneNtoS(int column, int row) {
List<GridObject> field = this.getFieldPosition(column, row);
for (GridObject g : field) {
if (g.getClass().equals(StreetObject.class)) {
return ((StreetObject) g).isLeftLaneNtoS();
}
}
return false;
}
@MethodRunnableProperties(ignoreMethod=true)
public boolean isLeftLaneWtoE(int column, int row) {
List<GridObject> field = this.getFieldPosition(column, row);
for (GridObject g : field) {
if (g.getClass().equals(StreetObject.class)) {
return ((StreetObject) g).isRightLaneWtoE();
}
}
return false;
}
@MethodRunnableProperties(ignoreMethod=true)
public boolean isRightLaneWtoE(int column, int row) {
List<GridObject> field = this.getFieldPosition(column, row);
for (GridObject g : field) {
if (g.getClass().equals(StreetObject.class)) {
return ((StreetObject) g).isLeftLaneWtoE();
}
}
return false;
}
@MethodRunnableProperties(ignoreMethod=true)
public boolean isCrossing(int column, int row) {
List<GridObject> field = this.getFieldPosition(column, row);
for (GridObject g : field) {
if (g.getClass().equals(StreetObject.class)) {
StreetObject sa = (StreetObject) g;
return sa.isCrossing();
}
}
return false;
}
@Override
@MethodRunnableProperties(ignoreMethod=true)
public synchronized BufferedImage getOutsideView() {
BufferedImage img = super.getOutsideView();
try {
AllroundVideoPlugin vid = (AllroundVideoPlugin) this.getSimTime().getPluginObject(new AllroundVideoPlugin().id());
CarAgent c = (CarAgent) this.getAgent(vid.getMarkedAgentId());
Graphics2D g = img.createGraphics();
g.setColor(Color.blue);
Polygon2D pol = new Polygon2D();
pol.add(new Vector2D(c.getTargetLocation()).add(new Vector2D(0.5, 0.5)));
pol.add(new Vector2D(c.getTargetLocation()).add(new Vector2D(-0.5, 0.5)));
pol.add(new Vector2D(c.getTargetLocation()).add(new Vector2D(-0.5, -0.5)));
pol.add(new Vector2D(c.getTargetLocation()).add(new Vector2D(0.5, -0.5)));
g.setStroke(new BasicStroke(5));
g.drawPolygon(this.getPolygonInVisualization(pol).toPol());
g.setStroke(new BasicStroke(2));
g.setColor(Color.red);
c.getTrace().forEach(v ->
{Vector2D a = this.getPointInVisualization(v);
g.drawOval((int) a.x - 3, (int) a.y - 3, 6, 6);});
} catch (Exception e) {}
return img;
}
public double getTargetsReachedPerTickPerCar() {
double sum = 0;
ArrayList<CarAgent> cars = this.getAllCars();
for (CarAgent c : cars) {
sum += c.getTargetCount();
}
return sum / this.getSimTime().getCurrentTime().getLastTick() / cars.size();
}
@MethodRunnableProperties(ignoreMethod=true)
public boolean isOnStreet(double x, double y) {
return this.getFieldPosition(new Vector2D(x, y)).size() > 0;
}
public double getAvgDistanceToTarget() {
double targetDist = 0;
ArrayList<CarAgent> cars = this.getAllCars();
for (CarAgent c : cars) {
targetDist += c.distanceToTarget();
}
return targetDist / cars.size();
}
}