package traffic3.manager; import traffic3.objects.TrafficArea; import traffic3.objects.TrafficBlockade; import traffic3.objects.TrafficAgent; import java.util.Collection; import java.util.Collections; import java.util.Set; import java.util.HashSet; import java.util.Map; import java.util.HashMap; import java.util.Properties; import java.util.List; import java.util.ArrayList; import rescuecore2.standard.entities.Human; import rescuecore2.standard.entities.Area; import rescuecore2.standard.entities.Blockade; import rescuecore2.standard.entities.StandardWorldModel; import rescuecore2.standard.entities.StandardEntity; import rescuecore2.worldmodel.Entity; import rescuecore2.worldmodel.EntityID; import rescuecore2.misc.collections.LazyMap; import com.infomatiq.jsi.SpatialIndex; import com.infomatiq.jsi.Rectangle; import com.infomatiq.jsi.IntProcedure; import com.infomatiq.jsi.rtree.RTree; /** The traffic manager maintains information about traffic simulator objects. */ public class TrafficManager { private Map<Integer, TrafficArea> areaByID; private Map<Integer, TrafficBlockade> blockadeByID; private Map<Area, TrafficArea> areas; private Map<Blockade, TrafficBlockade> blocks; private Map<Human, TrafficAgent> agents; private Map<TrafficArea, Collection<TrafficArea>> areaNeighbours; private SpatialIndex index; /** Construct a new TrafficManager. */ public TrafficManager() { areas = new HashMap<Area, TrafficArea>(); areaByID = new HashMap<Integer, TrafficArea>(); blocks = new HashMap<Blockade, TrafficBlockade>(); blockadeByID = new HashMap<Integer, TrafficBlockade>(); agents = new HashMap<Human, TrafficAgent>(); areaNeighbours = new LazyMap<TrafficArea, Collection<TrafficArea>>() { @Override public Collection<TrafficArea> createValue() { return new HashSet<TrafficArea>(); } }; index = new RTree(); index.init(new Properties()); } /** Find the area that contains a point. @param x The X coordinate. @param y The Y coordinate. @return The TrafficArea that contains the given point, or null if no such area is found. */ public TrafficArea findArea(double x, double y) { final List<TrafficArea> found = new ArrayList<TrafficArea>(); index.intersects(new Rectangle((float)x, (float)y, (float)x, (float)y), new IntProcedure() { @Override public boolean execute(int id) { found.add(areaByID.get(id)); return true; } }); for (TrafficArea next : found) { if (next.contains(x, y)) { return next; } } return null; } /** Get the neighbouring areas to a TrafficArea. @param area The area to look up. @return All TrafficAreas that share an edge with the given area. */ public Collection<TrafficArea> getNeighbours(TrafficArea area) { return areaNeighbours.get(area); } /** Get all agents in the same area or a neighbouring area as an agent. @param agent The agent to look up. @return All agents (except the input agent) that are in the same or a neighbouring area. */ public Collection<TrafficAgent> getNearbyAgents(TrafficAgent agent) { Set<TrafficAgent> result = new HashSet<TrafficAgent>(); result.addAll(agent.getArea().getAgents()); for (TrafficArea next : getNeighbours(agent.getArea())) { result.addAll(next.getAgents()); } result.remove(agent); return result; } /** Remove all objects from this manager. */ public void clear() { areas.clear(); blocks.clear(); agents.clear(); areaNeighbours.clear(); areaByID.clear(); blockadeByID.clear(); index = new RTree(); index.init(new Properties()); } /** Register a new TrafficArea. @param area The TrafficArea to register. */ public void register(TrafficArea area) { areas.put(area.getArea(), area); int id = area.getArea().getID().getValue(); areaByID.put(id, area); index.add(area.getBounds(), id); } /** Register a new TrafficAgent. @param agent The TrafficAgent to register. */ public void register(TrafficAgent agent) { agents.put(agent.getHuman(), agent); } /** Register a new TrafficBlockade. @param block The TrafficBlockade to register. */ public void register(TrafficBlockade block) { blocks.put(block.getBlockade(), block); blockadeByID.put(block.getBlockade().getID().getValue(), block); } /** Remove a blockade. @param block The TrafficBlockade to remove. */ public void remove(TrafficBlockade block) { remove(block.getBlockade()); } /** Remove a blockade. @param block The Blockade to remove. */ public void remove(Blockade block) { blocks.remove(block); blockadeByID.remove(block.getID().getValue()); } /** Get all TrafficAgents. @return All TrafficAgents. */ public Collection<TrafficAgent> getAgents() { return Collections.unmodifiableCollection(agents.values()); } /** Get all TrafficAreas. @return All TrafficAreas. */ public Collection<TrafficArea> getAreas() { return Collections.unmodifiableCollection(areas.values()); } /** Get all TrafficBlockades. @return All TrafficBlockades. */ public Collection<TrafficBlockade> getBlockades() { return Collections.unmodifiableCollection(blocks.values()); } /** Compute pre-cached information about the world. TrafficArea and TrafficAgent objects must have already been registered with {@link #register(TrafficArea)} and {@link #register(TrafficAgent)}. @param world The world model. */ public void cacheInformation(StandardWorldModel world) { areaNeighbours.clear(); for (StandardEntity next : world) { if (next instanceof Area) { computeNeighbours((Area)next, world); } } } /** Get the TrafficArea that wraps a given Area. @param a The area to look up. @return The TrafficArea that wraps the given area or null if no such TrafficArea exists. */ public TrafficArea getTrafficArea(Area a) { return areas.get(a); } /** Get the TrafficBlockade that wraps a given Blockade. @param b The blockade to look up. @return The TrafficBlockade that wraps the given blockade or null if no such TrafficBlockade exists. */ public TrafficBlockade getTrafficBlockade(Blockade b) { return blocks.get(b); } /** Get the TrafficAgent that wraps a given human. @param h The human to look up. @return The TrafficAgent that wraps the given human or null if no such TrafficAgent exists. */ public TrafficAgent getTrafficAgent(Human h) { return agents.get(h); } private void computeNeighbours(Area a, StandardWorldModel world) { Collection<TrafficArea> neighbours = areaNeighbours.get(getTrafficArea(a)); neighbours.clear(); for (EntityID id : a.getNeighbours()) { Entity e = world.getEntity(id); if (e instanceof Area) { neighbours.add(getTrafficArea((Area)e)); } } } }