from typing import Tuple,List,Dict import sys from datetime import datetime as dt import matplotlib matplotlib.use('QtAgg') import matplotlib.pyplot as plt from shapely import Polygon,Point,GeometryCollection,get_coordinates from shapely.plotting import plot_polygon import jupedsim as jps import numpy as np import pathlib from geometry_setup import GeometrySetup from spawning import SpawnManager from crosswalk_setup import CrosswalkStatus,CrosswalkController,CrosswalkState,CrosswalkConfig from agent_setup import AgentConfig from dataclasses import dataclass @dataclass class SimulationSetup: simulation_time:float=200.0 time_step:float=0.01 max_iterations:int=50000 trajectory_file:str="crosswalk_sim.sqlite" current_setup:bool=True trigger_dist:float=1.0 activation_time:float=30.0 cooldown_time:float=10.0 min_agents_activation:int=1 activation_delay:float=2.0 class CrosswalkSimulation: def __init__(self,config:SimulationSetup): self.config = config self.RED = "\033[1;31m" self.GREEN = "\033[1;32m" self.YELLOW = "\033[1;33m" self.BLUE = "\033[1;34m" self.PURPLE = "\033[1;35m" self.CYAN = "\033[1;36m" self.RESET = "\033[0m" print(f"{self.YELLOW}\nInitializing Geometry...") self.geo = GeometrySetup(self.config.current_setup) self.spawn_area = self.geo.spawn() self.queue_area = self.geo.queue() self.grass_area = self.geo.grass() self.crosswalk_area = self.geo.crosswalk() self.entry_area = self.geo.entry_polygon() self.exit_area = self.geo.exit_polygon() self.walkable_area = GeometryCollection([ self.spawn_area, self.queue_area, self.grass_area, self.crosswalk_area, ]) print(f"{self.GREEN}Geometry Configuration Complete!") print(f"{self.YELLOW}\nInitializing Agent Setup...") self.all_agents, pop_count = AgentConfig() self.total_agents = len(self.all_agents) self.req_spacing = (max([agent.radius for agent in self.all_agents])*2.2) print(f"{self.GREEN}Agent Setup Complete!") print(f"{self.RESET}Generated {self.total_agents} Agents") print(f"Population Breakdown:\n"+"".join(f"\t{k}: {v}\n" for k,v in pop_count.items())) print(f"{self.YELLOW}Initializing Spawn Manager...") self.spawn_manager = SpawnManager(self.spawn_area,min_spacing=self.req_spacing) self.all_coords = self.spawn_manager.generate_coords() print(f"{self.GREEN}Spawn Manager Setup Complete!") print(f"{self.RESET}Generated {len(self.all_coords)} Spawn Coordinates") print(f"{self.YELLOW}\nInitializing Crosswalk Controller...") self.crosswalk_controller = CrosswalkController( CrosswalkConfig( trigger_dist=self.config.trigger_dist, activation_time=self.config.activation_time, cooldown_time=self.config.cooldown_time, min_agents_activation=self.config.min_agents_activation, activation_delay=self.config.activation_delay, crosswalk_area=self.crosswalk_area, trigger_area=self.entry_area ), self.config.current_setup ) print(f"{self.GREEN}Crosswalk Controller Setup Complete!") print(f"{self.YELLOW}\nInitializing JuPedSim Model...") self.trajectory_file = self.config.trajectory_file self.simulation = jps.Simulation( model=jps.CollisionFreeSpeedModel(), geometry=self.walkable_area, trajectory_writer=jps.SqliteTrajectoryWriter( output_file=pathlib.Path(self.trajectory_file) ), dt=self.config.time_step ) self.journey_setup() self.jps_agent_ids:Dict[int,int] = {} self.iteration_count = 0 self.current_time = 0.0 print(f"{self.GREEN}JuPedSim Model Initialized{self.RESET}") print(f"\tTime Step: {self.config.time_step}s") print(f"\tMax Iterations: {self.config.max_iterations}") print(f"\tCrosswalk Activation Length: {self.config.activation_time}s") print(f"\tCrosswalk Cooldown Length: {self.config.cooldown_time}s") def journey_setup(self): self.queue_waiting_coords = self.waiting_coords( self.queue_area, self.spawn_manager.min_spacing ) self.queue_stage_id = self.simulation.add_waiting_set_stage( self.queue_waiting_coords ) self.queue_stage = self.simulation.get_stage(self.queue_stage_id) self.queue_stage.state = jps.WaitingSetState.ACTIVE self.crosswalk_entry_id = self.simulation.add_waypoint_stage(( self.entry_area.centroid.x, self.entry_area.centroid.y), self.spawn_manager.min_spacing ) self.crosswalk_exit_id = self.simulation.add_waypoint_stage(( self.exit_area.centroid.x, self.exit_area.centroid.y), self.spawn_manager.min_spacing ) self.exit_stage_id = self.simulation.add_waypoint_stage(( self.grass_area.centroid.x, self.grass_area.centroid.y), self.spawn_manager.min_spacing ) self.journey = jps.JourneyDescription([ self.queue_stage_id, self.crosswalk_entry_id, self.crosswalk_exit_id, self.exit_stage_id ]) self.journey.set_transition_for_stage( self.queue_stage_id, jps.Transition.create_fixed_transition( self.crosswalk_entry_id )) self.journey.set_transition_for_stage( self.crosswalk_entry_id, jps.Transition.create_fixed_transition( self.crosswalk_exit_id )) self.journey.set_transition_for_stage( self.crosswalk_exit_id, jps.Transition.create_fixed_transition( self.exit_stage_id )) self.journey_id = self.simulation.add_journey( self.journey) def waiting_coords(self,area:Polygon,spacing:float)->List[Tuple[float,float]]: min_x,min_y,max_x,max_y = area.bounds points = [] x = (min_x+spacing)/2 while x