From b1531078637aad3906d9cedc6b6f9b5d7262950f Mon Sep 17 00:00:00 2001 From: Mohcine Chraibi Date: Tue, 12 Mar 2024 08:40:52 +0100 Subject: [PATCH] simplify UI and usage --- app.py | 65 +++++++++++++++++++++++++++---------------- files/bottleneck.json | 40 ++++++++------------------ requirements.txt | 8 ------ simulation.py | 22 ++++++++------- src/analysis.py | 16 ++++------- src/inifile_parser.py | 14 ++++++++++ src/ui.py | 60 +++++++++++++++++++++------------------ src/utilities.py | 8 ++++-- 8 files changed, 123 insertions(+), 110 deletions(-) delete mode 100644 requirements.txt diff --git a/app.py b/app.py index 17e5b33..a9f811a 100644 --- a/app.py +++ b/app.py @@ -66,49 +66,66 @@ def read_data(output_file: str) -> pd.DataFrame: # st.sidebar.info(f"{jps.__version__ = }") # st.sidebar.info(f"{pedpy.__version__ = }") - if tab == "Initialisation": - column_1, column_2 = st.columns((1, 1)) - file_name = column_1.text_input( - "Load config file: ", value="files/bottleneck.json" + if tab == "Simulation": + with st.sidebar.expander("Save/load config"): + column_1, column_2 = st.columns((1, 1)) + + file_name = str( + column_1.selectbox( + "Load", + sorted(list(set(st.session_state.all_files)), reverse=True), + ) ) + + # file_name = column_1.text_input( + # "Load", value="files/bottleneck.json", help="Load config file" + # ) json_file = Path(file_name) data = {} if not json_file.exists(): st.error(f"file: {file_name} does not exist!") st.stop() - with column_1: - data = load_json(json_file) - ui_velocity_model_parameters(data) - ui_simulation_parameters(data) - ui_motivation_parameters(data) - st.session_state.data = data - st.session_state.all_files.append(file_name) + # with column_1: + data = load_json(json_file) + ui_velocity_model_parameters(data) + ui_simulation_parameters(data) + ui_motivation_parameters(data) + st.session_state.data = data + st.session_state.all_files.append(file_name) # Save Button (optional) new_json_name = column_2.text_input( - "Save config file: ", value="files/bottleneck.json" + "Save", help="Save config file: ", value="files/bottleneck2.json" ) new_json_file = Path(new_json_name) - if column_2.button( - "Save config", - help=f"After changing the values, you can save the configs in a separate file ({new_json_name})", - ): - save_json(new_json_file, data) - column_1.info(f"Saved file as {new_json_name}") - st.session_state.all_files.append(new_json_name) - - if column_2.button("Reset", help="Delete all trajectory files"): + save_json(new_json_file, data) + # if column_1.button( + # "Save config", + # help=f"After changing the values, you can save the configs in a separate file ({new_json_name})", + # ): + # save_json(new_json_file, data) + # st.sidebar.info(f"Saved file as {new_json_name}") + st.session_state.all_files.append(new_json_name) + + if column_2.button("Delete files", help="Delete all trajectory files"): delete_txt_files() # Run Simulation if tab == "Simulation": - msg = st.sidebar.empty() c1, c2, c3 = st.columns(3) - OUTPUT_FILE = c1.text_input("Result: ", value="files/trajectory.sqlite") + msg = st.empty() CONFIG_FILE = str( - c2.selectbox("Select config file", list(set(st.session_state.all_files))) + c2.selectbox( + "Select config file", + sorted(list(set(st.session_state.all_files)), reverse=True), + ) ) + strategy = data["motivation_parameters"]["motivation_strategy"] + name, extension = CONFIG_FILE.rsplit(".", 1) + sqlite_filename = f"{name}_{strategy}.{extension.replace('json', 'sqlite')}" + OUTPUT_FILE = c1.text_input("Result: ", value=f"{sqlite_filename}") + fps = c3.number_input( "fps", min_value=1, max_value=32, value=8, help="show every nth frame" ) diff --git a/files/bottleneck.json b/files/bottleneck.json index 8042096..12dc4c7 100644 --- a/files/bottleneck.json +++ b/files/bottleneck.json @@ -5,25 +5,17 @@ ], "version": 0.1, "velocity_init_parameters": { - "a_ped": 0.29, - "d_ped": 0.11, - "a_wall": 0.5, - "d_wall": 0.02 + "a_ped": 1.0, + "d_ped": 0.2, + "a_wall": 1.0, + "d_wall": 0.2, + "radius": 0.10 }, - "velocity_model_parameter_profiles": [ - { - "id": 1.0, - "time_gap": 1.0, - "tau": 0.5, - "v0": 1.2, - "radius": 0.15 - } - ], "simulation_parameters": { "fps": 60, "time_step": 0.01, "number_agents": 50, - "simulation_time": 50 + "simulation_time": 100 }, "measurement_line": { "vertices": [ @@ -59,7 +51,7 @@ }, "motivation_parameters": { "motivation_strategy": "default", - "active": 1, + "active": 0, "normal_v_0": 1.2, "normal_time_gap": 1.0, "width": "1.0", @@ -84,14 +76,6 @@ } ] }, - "grid_parameters": { - "min_v_0": 1, - "max_v_0": 2, - "v_0_step": 0.1, - "min_time_gap": 0.1, - "max_time_gap": 1, - "time_gap_step": 0.1 - }, "accessible_areas": [ { "id": 1, @@ -119,19 +103,19 @@ "vertices": [ [ 60.0, - 101.4 + 101.7 ], [ 62.0, - 101.4 + 101.7 ], [ 62.0, - 102.6 + 102.3 ], [ 60.0, - 102.6 + 102.3 ] ] }, @@ -204,4 +188,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 3000d32..0000000 --- a/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -streamlit -shapely -jsonschema -jupedsim -plotly -pedpy -numpy -streamlit_option_menu diff --git a/simulation.py b/simulation.py index d5926b1..d3bd603 100644 --- a/simulation.py +++ b/simulation.py @@ -20,6 +20,7 @@ parse_destinations, parse_distribution_polygons, parse_fps, + parse_radius, parse_motivation_doors, parse_motivation_parameter, parse_motivation_strategy, @@ -84,6 +85,8 @@ def init_simulation( geometry = build_geometry(accessible_areas) # areas = build_areas(destinations, labels) a_ped, d_ped, a_wall, d_wall = parse_velocity_init_parameters(_data) + normal_v_0 = parse_normal_v_0(_data) + normal_time_gap = parse_normal_time_gap(_data) simulation = jps.Simulation( model=jps.CollisionFreeSpeedModel( strength_neighbor_repulsion=a_ped, @@ -97,8 +100,6 @@ def init_simulation( output_file=pathlib.Path(_trajectory_path), every_nth_frame=_fps ), ) - normal_v_0 = parse_normal_v_0(_data) - normal_time_gap = parse_normal_time_gap(_data) motivation_doors = parse_motivation_doors(_data) if not motivation_doors: log_error("json file does not contain any motivation door") @@ -163,7 +164,6 @@ def run_simulation( if motivation_model.active and simulation.iteration_count() % 100 == 0: agents = simulation.agents() number_agents_in_simulation = simulation.agent_count() - logging.info(f"{number_agents_in_simulation = }") for agent in agents: position = agent.position distance = ( @@ -208,12 +208,6 @@ def main( :param trajectory_file: :returns: """ - print("main") - print(f"{_number_agents = }") - print(f"{_fps = }") - print(f"{ _time_step = }") - print(f"{_simulation_time = }") - print(f"{ _trajectory_path = }") simulation, motivation_model = init_simulation( _data, _time_step, _fps, _trajectory_path ) @@ -239,8 +233,16 @@ def main( if not total_agents: break + normal_v_0 = parse_normal_v_0(_data) + normal_time_gap = parse_normal_time_gap(_data) + radius = parse_radius(_data) agent_parameters = jps.CollisionFreeSpeedModelAgentParameters( - journey_id=journey_id, stage_id=stage_id, radius=0.2 + journey_id=journey_id, + stage_id=stage_id, + radius=radius, + v0=normal_v_0, + time_gap=normal_time_gap + ) ped_ids = distribute_and_add_agents(simulation, agent_parameters, positions) diff --git a/src/analysis.py b/src/analysis.py index 1b2256e..ab750d0 100644 --- a/src/analysis.py +++ b/src/analysis.py @@ -71,25 +71,21 @@ def run(): selected = st.sidebar.radio( "Choose option", [ - "Heatmap", + "Distance to entrance", + "Speed", "Density", "Flow", - "Speed", "NT", "Voronoi polygons", - "Distance to entrance", + "Heatmap", ], ) SELECTED_OUTPUT_FILE = st.selectbox( - "Select file", list(set(glob.glob("files/*.sqlite"))) + "Select file", sorted(list(set(glob.glob("files/*.sqlite"))), reverse=True) ) - CONFIG_FILE = str( - st.selectbox("Select config file", list(set(st.session_state.all_files))) - ) - traj, walkable_area = read_sqlite_file(SELECTED_OUTPUT_FILE) - with open(CONFIG_FILE, "r", encoding="utf8") as f: + with open("files/bottleneck.json", "r", encoding="utf8") as f: json_str = f.read() json_data = json.loads(json_str) ui_measurement_parameters(json_data) @@ -129,7 +125,7 @@ def run(): ma_alpha=0.2, ).set_aspect("equal") fig = plt.gcf() - st.pyplot(fig) + st.sidebar.pyplot(fig) if selected == "NT": nt, crossing_frames = pedpy.compute_n_t( traj_data=traj, diff --git a/src/inifile_parser.py b/src/inifile_parser.py index 0e2e58a..3b6666e 100644 --- a/src/inifile_parser.py +++ b/src/inifile_parser.py @@ -55,6 +55,20 @@ def parse_velocity_init_parameters( return (8.0, 0.1, 5.0, 0.02) +def parse_radius( + json_data: Dict[str, Any], +) -> float: + """Parse radius. + + return radius + """ + if "velocity_init_parameters" in json_data: + return float(json_data["velocity_init_parameters"]["radius"]) + + else: + return 0.3 + + def parse_way_points( json_data: Dict[str, Any], ) -> List[Tuple[Point, float]]: diff --git a/src/ui.py b/src/ui.py index d24a460..1a83679 100644 --- a/src/ui.py +++ b/src/ui.py @@ -9,8 +9,8 @@ def init_sidebar() -> Any: """Init sidebar and 4 tabs.""" return option_menu( - "A well-motivated model for pedestrians", - ["Initialisation", "Simulation", "Analysis"], + "", + ["Simulation", "Analysis"], icons=[ "info-square", "pin-map", @@ -35,7 +35,7 @@ def init_sidebar() -> Any: def ui_measurement_parameters(data: Dict[str, Any]) -> None: """Measurement lines, polygons.""" - with st.expander("Measurement Parameters"): + with st.sidebar.expander("Measurement Parameters", expanded=True): st.code("Measurement line:") column_1, column_2 = st.columns((1, 1)) line = data["measurement_line"]["vertices"] @@ -57,26 +57,28 @@ def ui_measurement_parameters(data: Dict[str, Any]) -> None: def ui_velocity_model_parameters(data: Dict[str, Any]) -> None: """Set velocity Parameters Section.""" - with st.expander("Velocity model Parameters"): - data["velocity_init_parameters"]["a_ped"] = st.slider( + with st.sidebar.expander("Velocity model Parameters", expanded=False): + c1, c2 = st.columns(2) + data["velocity_init_parameters"]["a_ped"] = c1.number_input( "a_ped:", min_value=0.0, max_value=10.0, value=data["velocity_init_parameters"]["a_ped"], ) - data["velocity_init_parameters"]["d_ped"] = st.slider( + data["velocity_init_parameters"]["d_ped"] = c2.number_input( "d_ped:", min_value=0.01, max_value=1.0, value=data["velocity_init_parameters"]["d_ped"], ) - data["velocity_init_parameters"]["a_wall"] = st.slider( + data["velocity_init_parameters"]["a_wall"] = c1.number_input( "a_wall:", min_value=0.0, max_value=10.0, + step=0.1, value=data["velocity_init_parameters"]["a_wall"], ) - data["velocity_init_parameters"]["d_wall"] = st.slider( + data["velocity_init_parameters"]["d_wall"] = c2.number_input( "d_wall:", min_value=0.01, max_value=1.0, @@ -86,8 +88,8 @@ def ui_velocity_model_parameters(data: Dict[str, Any]) -> None: def ui_simulation_parameters(data: Dict[str, Any]) -> None: """Set simulation Parameters Section.""" - with st.expander("Simulation Parameters"): - data["simulation_parameters"]["fps"] = st.slider( + with st.sidebar.expander("Simulation Parameters"): + data["simulation_parameters"]["fps"] = st.number_input( "FPS:", min_value=1, max_value=60, @@ -102,50 +104,52 @@ def ui_simulation_parameters(data: Dict[str, Any]) -> None: data["simulation_parameters"]["simulation_time"] = st.number_input( "Simulation Time:", value=data["simulation_parameters"]["simulation_time"] ) + data["motivation_parameters"]["seed"] = st.text_input( + "Seed", + key="seed", + value=float(data["motivation_parameters"]["seed"]), + help="Seed for random generator for value", + ) def ui_motivation_parameters(data: Dict[str, Any]) -> None: """Motivation Parameters Section.""" - with st.expander("Motivation Parameters"): - motivation_activated = st.checkbox("Activate motivation") + with st.sidebar.expander("Motivation Parameters", expanded=True): + act = st.empty() + model = st.empty() + c1, c2 = st.columns(2) + motivation_activated = act.checkbox("Activate motivation", value=True) if motivation_activated: data["motivation_parameters"]["active"] = 1 else: data["motivation_parameters"]["active"] = 0 - motivation_strategy = st.selectbox( + motivation_strategy = model.selectbox( "Select model", ["default", "EVC"], help="Model 2: M = M(dist). Model 3: M = V.E, Model4: M=V.E.C", ) - data["motivation_parameters"]["width"] = st.text_input( + data["motivation_parameters"]["width"] = c1.text_input( "Width", key="width", value=float(data["motivation_parameters"]["width"]), help="width of function defining distance dependency", ) - data["motivation_parameters"]["height"] = st.text_input( + data["motivation_parameters"]["height"] = c2.text_input( "Height", key="hight", value=float(data["motivation_parameters"]["height"]), help="Height of function defining distance dependency", ) - data["motivation_parameters"]["seed"] = st.text_input( - "Seed", - key="seed", - value=float(data["motivation_parameters"]["seed"]), - help="Seed for random generator for value", - ) - - data["motivation_parameters"]["max_value"] = st.text_input( + data["motivation_parameters"]["max_value"] = c1.number_input( "Max_value", key="max_value", value=float(data["motivation_parameters"]["max_value"]), help="Max Value", ) - data["motivation_parameters"]["min_value"] = st.text_input( + data["motivation_parameters"]["min_value"] = c2.number_input( "Min_value", key="min_value", value=float(data["motivation_parameters"]["min_value"]), @@ -153,19 +157,21 @@ def ui_motivation_parameters(data: Dict[str, Any]) -> None: ) data["motivation_parameters"]["motivation_strategy"] = motivation_strategy - data["motivation_parameters"]["normal_v_0"] = st.slider( + data["motivation_parameters"]["normal_v_0"] = c1.number_input( "Normal V0:", - min_value=0.5, + min_value=0.1, max_value=2.5, value=float(data["motivation_parameters"]["normal_v_0"]), ) - data["motivation_parameters"]["normal_time_gap"] = st.slider( + data["motivation_parameters"]["normal_time_gap"] = c2.number_input( "Normal Time Gap:", min_value=0.1, max_value=3.0, step=0.1, value=float(data["motivation_parameters"]["normal_time_gap"]), ) + + with st.sidebar.expander(label="Door"): column_1, column_2 = st.columns((1, 1)) for door_idx, door in enumerate( data["motivation_parameters"]["motivation_doors"] diff --git a/src/utilities.py b/src/utilities.py index cc492dc..a8a7af9 100644 --- a/src/utilities.py +++ b/src/utilities.py @@ -23,10 +23,12 @@ def delete_txt_files(): - """Delete all *.txt files in the current directory.""" - files = glob.glob("*.txt") - + """Delete all *.sqlite files in the current directory.""" + files = glob.glob("files/*.sqlite") + if not files: + st.toast(f"No trajectories to delete!", icon="💿") for file in files: + st.toast(f"Delete {file}", icon="💿") try: os.remove(file) except Exception as e: