package trafficdefinition;
import classes.Pair;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import roadnetwork.Edge;
import roadnetwork.Lane;
import shapes.Line2DExt;
import shapes.Point2DExt;
import shapes.Rectangle2DExt;
/**
* Class representing an accident. An accident closes specific lanes of an edge by setting a vehicle to stop at a specified point on the lane. The accident is drawn on screen using an X on the edge.
*/
public class Accident extends TrafficDefinitionElement implements Serializable {
private static final long serialVersionUID = 1L;
/**
* The edge on which the accident happens
*/
private Edge edge = null;
/**
* The exact location where the accident happens
*/
private Point2DExt location = null;
/**
* The relative location of the accident from the edge starting point
*/
private float edgeRelativeLocation = 0;
/**
* The time that the edge re-opens
*/
private int endingTime;
/**
* The shape to draw for the accident
*/
private Shape shape = null;
/**
* The time that the accident happens and the edge closes
*/
private int startingTime;
/**
* The lanes of the edge affected by the accident.
*/
private List<Pair<Lane,Boolean>> affectedLanes = new ArrayList<Pair<Lane,Boolean>>();
/**
* Class constructor that creates a new accident as a copy of the specified
* accident
*
* @param source
*/
public Accident(Accident source) {
super(source);
startingTime = source.startingTime;
endingTime = source.endingTime;
edge = source.edge;
shape = source.shape;
location = source.location;
edgeRelativeLocation = source.edgeRelativeLocation;
for (Pair<Lane,Boolean> al : source.affectedLanes) {
affectedLanes.add(new Pair<Lane, Boolean>(al.getFirst(),al.getSecond().booleanValue()));
}
//Set the accident to be dependent on the edge so that the edge can not be deleted
edge.AddDependentTrafficElement(this);
}
/**
* Default constructor.
*/
public Accident(Edge edge, Point2D.Double location) {
super();
name = "Accident " + String.valueOf(increment++);
this.edge = edge;
this.location = new Point2DExt(location);
shape = new Rectangle2DExt(location.x-10,location.y-10,20,20);
//Find the point on the edge where the accident happens relative to the edge starting point
Point2D edgeStartingPoint = edge.getLanes().get(0).GetPoint(0);
double relativeLocation = Math.min(edgeStartingPoint.distance(this.location),edge.getLength());
if(relativeLocation>edge.getLength()){
edgeRelativeLocation = edge.getLength()/2f;
}
else{
edgeRelativeLocation = (float)relativeLocation;
}
// Close all lanes
for(Lane l:edge.getLanes()){
affectedLanes.add(new Pair<Lane, Boolean>(l,true));
}
//Set the accident to be dependent on the edge so that the edge can not be deleted
edge.AddDependentTrafficElement(this);
//TODO Should set dependencies to connected edges
}
@Override
public void Draw(Graphics2D g, double zoomFactor, boolean isDeleted, boolean isSelected) {
// Save the previous color to restore it later
Color prev = g.getColor();
if (!isSelected) {
g.setColor(Color.RED);
}
if (isSelected) {
g.setColor(Color.GREEN);
}
// Draw the shape
Line2DExt l1= new Line2DExt(new Point2DExt(location.x-10,location.y-10),new Point2DExt(location.x+10,location.y+10));
Line2DExt l2= new Line2DExt(new Point2DExt(location.x-10,location.y+10),new Point2DExt(location.x+10,location.y-10));
Stroke previousStroke = g.getStroke();
g.setStroke(new BasicStroke(6,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER));
g.draw(l1);
g.draw(l2);
//g.fill(shape);
g.setColor(prev);
g.setStroke(previousStroke);
}
@Override
public TrafficDefinitionElement elementClone() {
return new Accident(this);
}
/**
* @return the endingTime
* @uml.property name="endingTime"
*/
public int getEndingTime() {
return endingTime;
}
/**
* @return the startingTime
* @uml.property name="startingTime"
*/
public int getStartingTime() {
return startingTime;
}
@Override
public String getToolTip() {
String tooltip = "<html>" + "<b>Accident</b> " + name + "<br>" + "<b>Starting time: </b> " + String.valueOf(startingTime) + "<br>" + "<b>Ending time: </b>" + String.valueOf(endingTime);
return tooltip;
}
public Rectangle2DExt getBounds() {
return new Rectangle2DExt((Rectangle2D.Double) shape.getBounds2D());
}
public void handleLocationChanged(Handle h) {
}
@Override
public boolean Hit(Point p) {
if (shape.getBounds2D().contains(p.x, p.y)) {
if (shape.contains(p.x, p.y)) {
return true;
}
}
return false;
}
@Override
public boolean Hit(Rectangle transformedRectangle) {
return shape.intersects(transformedRectangle);
}
/**
* @param endingTime the endingTime to set
* @uml.property name="endingTime"
*/
public void setEndingTime(int endingTime) {
this.endingTime = endingTime;
}
/**
* @param startingTime the startingTime to set
* @uml.property name="startingTime"
*/
public void setStartingTime(int startingTime) {
this.startingTime = startingTime;
}
/**
* @return the edge
* @uml.property name="edge"
*/
public Edge getEdge() {
return edge;
}
/**
* Returns the affected lanes from the accident.
* @uml.property name="affectedLanes"
*/
public List<Pair<Lane,Boolean>> getAffectedLanes() {
return affectedLanes;
}
/**
* @return the edgeRelativeLocation
* @uml.property name="edgeRelativeLocation"
*/
public float getEdgeRelativeLocation() {
return edgeRelativeLocation;
}
@Override
public String toXML() {
StringBuilder sb=new StringBuilder();
sb.append("<accident name=\""+name+"\" streetName=\""+edge.getId()+"\" startingTime=\""+String.valueOf(startingTime)+"\" endingTime=\""+String.valueOf(endingTime)+"\" location=\""+String.valueOf(edgeRelativeLocation)+"\" >\n");
sb.append("<affectedLanes>\n");
for(Pair<Lane,Boolean> l:affectedLanes){
if(l.getSecond()){
sb.append("<lane name=\""+l.getFirst().getId()+"\" />\n");
}
}
sb.append("</affectedLanes>\n");
sb.append("</accident>\n");
return sb.toString();
}
}