Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ For now, you can inspect the data structures and files in the examples provided
* 📁 `FleetPy/studies`

More detailed descriptions of the **data structure, preprocessing steps, and result data** will be provided in upcoming versions.
Additionally, a **GUI to set up scenarios** (with a choice of submodules and data) is planned for the future. 🎨

In general, you can **save your data and study definitions** in the mentioned directories. These are included in `.gitignore`.

Expand All @@ -118,6 +117,28 @@ In general, you can **save your data and study definitions** in the mentioned di

---

## 🖥️ Using the FleetPy GUI

FleetPy provides a graphical user interface (GUI) for creating scenarios, running simulations, and visualizing results.

To launch the GUI:

1. Activate your Conda environment:
```bash
conda activate fleetpy
```
2. Start the GUI with:
```bash
streamlit run streamlit_gui.py
```

This will open the FleetPy GUI in your web browser, where you can:
- **Create new simulation scenarios** with guided parameter selection.
- **Run simulations** by uploading or selecting configuration files.
- **Visualize results** interactively.

---

## 📊 Running a Simulation

To test an example scenario:
Expand Down
2 changes: 2 additions & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ dependencies:
- shapely==2.0.6
- tqdm==4.67.1
- dill==0.3.9
- streamlit==1.28.0
- contextily==1.4.0
71 changes: 71 additions & 0 deletions src/scenario_gui/parameter_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""Utility functions for handling parameters in the FleetPy scenario GUI."""
import os
from typing import Dict, List, Optional, Any

def get_abnormal_param_options(param: str, fleetpy_path: str) -> Optional[List[str]]:
"""Get special options for specific parameters that need to be populated from the filesystem.

Args:
param: The parameter name to get options for
fleetpy_path: The path to the FleetPy installation

Returns:
A list of options if the parameter has special handling, None otherwise
"""
if param == "network_name":
path = os.path.join(fleetpy_path, "data", "networks")
return [""] + os.listdir(path)
elif param == "demand_name":
path = os.path.join(fleetpy_path, "data", "demand")
return [""] + os.listdir(path)
elif param == "rq_file":
# Will be populated based on network and demand selection
return [""]
return None

def categorize_parameters(param_names: List[str], param_dict: Dict[str, Any]) -> Dict[str, List[str]]:
"""Categorize parameters into logical groups based on their prefixes and meanings.

Args:
param_names: List of parameter names to categorize
param_dict: Dictionary of parameter objects with metadata

Returns:
Dictionary mapping category names to lists of parameter names
"""
categories = {
"Basic Settings": [],
"Time Settings": [],
"Request Settings": [],
"Operator Settings": [],
"Parcel Settings": [],
"Vehicle Settings": [],
"Infrastructure": [],
"Other": []
}

for param in param_names:
param_obj = param_dict.get(param)
if not param_obj:
continue

if param.startswith(("start_time", "end_time", "time_step", "lock_time")):
categories["Time Settings"].append(param)
elif param.startswith("user_") or "wait_time" in param or "detour" in param:
categories["Request Settings"].append(param)
elif param.startswith("op_"):
if "parcel" in param:
categories["Parcel Settings"].append(param)
else:
categories["Operator Settings"].append(param)
elif param in ["network_name", "demand_name", "rq_file", "scenario_name", "study_name"]:
categories["Basic Settings"].append(param)
elif param.startswith("veh_") or "vehicle" in param or "fleet" in param:
categories["Vehicle Settings"].append(param)
elif param.startswith(("zone_", "infra_")):
categories["Infrastructure"].append(param)
else:
categories["Other"].append(param)

# Remove empty categories
return {k: v for k, v in categories.items() if v}
117 changes: 117 additions & 0 deletions src/scenario_gui/ui_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"""UI utility functions for the FleetPy scenario GUI."""
import streamlit as st
from typing import Any, Optional

def render_parameter_input(param: str, param_obj: Any, key_prefix: str = "") -> Optional[str]:
"""Render the appropriate input widget for a parameter based on its type and metadata.

Args:
param: The parameter name to render
param_obj: The parameter object containing metadata
key_prefix: Prefix for the Streamlit widget key

Returns:
The value from the input widget, or None if no value was entered
"""
help_text = param_obj.doc_string if hasattr(param_obj, 'doc_string') else ""
if hasattr(param_obj, 'type') and param_obj.type:
type_info = f" (Expected type: {param_obj.type})"
help_text = f"{help_text}{type_info}" if help_text else type_info
default_value = param_obj.default_value if hasattr(param_obj, 'default_value') else None
param_type = param_obj.type if hasattr(param_obj, 'type') else "str"

if hasattr(param_obj, 'options') and param_obj.options:
options = ["None"] if key_prefix == "optional_" else ["Choose..."]
options.extend(param_obj.options)
value = st.selectbox(
f"{param}",
options=options,
key=f"param_{key_prefix}{param}",
help=help_text
)
if value not in ["None", "Choose..."]:
return value
elif param_type == "int":
try:
default = int(default_value) if default_value and str(default_value).strip() else 0
except (ValueError, TypeError):
default = 0
value = st.number_input(
f"{param}",
value=default,
key=f"param_{key_prefix}{param}",
help=help_text
)
return str(value)
elif param_type == "float":
try:
default = float(default_value) if default_value and str(default_value).strip() else 0.0
except (ValueError, TypeError):
default = 0.0
value = st.number_input(
f"{param}",
value=default,
key=f"param_{key_prefix}{param}",
help=help_text
)
return str(value)
elif param_type == "bool":
value = st.checkbox(
f"{param}",
value=bool(default_value) if default_value is not None else False,
key=f"param_{key_prefix}{param}",
help=help_text
)
return str(value)
else:
value = st.text_input(
f"{param}",
value=str(default_value) if default_value is not None else "",
key=f"param_{key_prefix}{param}",
help=help_text
)
return value if value else None

def apply_sidebar_styles() -> None:
"""Apply custom CSS styles to the Streamlit sidebar."""
st.sidebar.markdown("""
<style>
section[data-testid="stSidebar"] > div {
padding-top: 0;
}
section[data-testid="stSidebar"] .block-container {
padding-top: 2rem;
padding-left: 1.5rem;
padding-right: 1.5rem;
}
section[data-testid="stSidebar"] button[kind="secondary"] {
background: none;
text-align: left;
font-weight: normal;
padding: 0.5rem 0.75rem;
color: #262730;
width: 100%;
display: flex;
align-items: center;
gap: 0.5rem;
border: none;
border-left: 2px solid transparent;
border-radius: 0;
margin: 0;
justify-content: flex-start;
min-height: 40px;
transition: border-left-color 0.2s ease;
}
section[data-testid="stSidebar"] button[kind="secondary"]:hover {
border-left-color: rgba(255, 75, 75, 0.3);
}
section[data-testid="stSidebar"] button[kind="secondary"][data-active="true"] {
color: rgb(255, 75, 75);
border-left-color: rgb(255, 75, 75);
font-weight: 600;
}
section[data-testid="stSidebar"] div.stTitle:first-child {
margin-bottom: 2rem;
}
</style>
""", unsafe_allow_html=True)
Loading