Skip to content

Commit

Permalink
Merge pull request #4 from k8culver/feature/loading-map-feedback-overlay
Browse files Browse the repository at this point in the history
Greyed Out Map Loading Feedback
  • Loading branch information
k8culver authored Apr 12, 2024
2 parents 60a1a02 + 0816c28 commit 1726516
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 43 deletions.
10 changes: 5 additions & 5 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ def generate_inital_map(num_clients: int) -> folium.Map:
Returns:
folium.Map: Initial map shown on the map tab.
"""
map_network, depot_id, force_locations = generate_mapping_information(num_clients)
initial_map = show_locations_on_initial_map(map_network, depot_id, force_locations)
map_network, depot_id, force_locations, map_bounds = generate_mapping_information(num_clients)
initial_map = show_locations_on_initial_map(map_network, depot_id, force_locations, map_bounds)
return initial_map


Expand Down Expand Up @@ -189,7 +189,7 @@ def update_tables(
(Output("cancel-button", "style"), {"display": "inline-block"}, {"display": "none"}),
(Output("run-button", "style"), {"display": "none"}, {"display": "inline-block"}),
(Output("results-tab", "disabled"), True, False),
(Output("results-tab", "className"), "tab-loading", "tab"),
(Output("results-tab", "label"), "Loading...", "Results"),
# switch to map tab while running
(Output("tabs", "value"), "map-tab", "map-tab"),
# block certain callbacks from running until this is done
Expand Down Expand Up @@ -253,8 +253,8 @@ def run_optimization(
sampler_type = SamplerType(sampler_type)

if ctx.triggered_id == "run-button":
map_network, depot_id, force_locations = generate_mapping_information(num_clients)
initial_map = show_locations_on_initial_map(map_network, depot_id, force_locations)
map_network, depot_id, force_locations, map_bounds = generate_mapping_information(num_clients)
initial_map = show_locations_on_initial_map(map_network, depot_id, force_locations, map_bounds)

routing_problem_parameters = RoutingProblemParameters(
map_network=map_network,
Expand Down
46 changes: 15 additions & 31 deletions assets/mvrp.css
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,17 @@ th:last-child, td:last-child {
height: 100%;
}

#solution-map[data-dash-is-loading="true"] {
filter: grayscale(1);
opacity: 0.3;
transition: filter 0.1s linear, opacity 0.1s linear;
}

div.dash-sk-circle {
height: 6rem;
width: 6rem;
}

#control-card > p {
font-weight: bold;
}
Expand Down Expand Up @@ -390,41 +401,14 @@ div.tab.tab--disabled {
background-color: white;
}

.tab-content > div {
visibility: visible !important;
}

.tab-content--results {
padding: 0 3rem;
}

#solver-time-limit {
margin-bottom: 2rem;
}

.tab-loading {
overflow: hidden;
position: relative;
}

.tab-loading:before {
content: "";
position: absolute;
bottom: calc(-100% + 5px);
left: calc(-100% + 5px);
width: 100%;
height: 100%;
background: var(--teal-dark);
animation: rotate 4s linear infinite;
}

@keyframes rotate {
20% {
transform: translate(0px, calc(-200% + 10px))
}
50% {
transform: translate(calc(200% - 10px), calc(-200% + 10px))
}
70% {
transform: translate(calc(200% - 10px), 0px)
}
100% {
transform: translate(0px, 0px)
}
}
9 changes: 8 additions & 1 deletion dash_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,14 @@ def set_html(app):
id="map-tab",
value="map-tab", # used for switching to programatically
className="tab",
children=html.Iframe(id="solution-map")
children=[
dcc.Loading(
id="loading",
type="circle",
color="#2A7DE1",
children=html.Iframe(id="solution-map")
),
],
),
dcc.Tab(
label="Results",
Expand Down
26 changes: 20 additions & 6 deletions map.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import folium.plugins as plugins
import networkx as nx
import numpy as np
from numpy.typing import NDArray
import osmnx as ox
from scipy.spatial import cKDTree

Expand All @@ -36,19 +37,25 @@
depot_icon = folium.CustomIcon(str(depot_icon_path), icon_size=(32, 37))


def _find_node_index_central_to_network(node_index_map: dict) -> int:
"""Finds node index central to network."""
def _get_coordinates(node_index_map: dict) -> NDArray:
"""Returns an array of coordinates for all nodes."""
coordinates = np.zeros((len(node_index_map), 2))
for node_index, node in node_index_map.items():
coordinates[node_index][0] = node[1]["y"]
coordinates[node_index][1] = node[1]["x"]

return coordinates


def _find_node_index_central_to_network(node_index_map: dict) -> int:
"""Finds node index central to network."""
coordinates = _get_coordinates(node_index_map)
centroid = np.sum(coordinates, 0) / len(node_index_map)
kd_tree = cKDTree(coordinates)
return kd_tree.query(centroid)[1]


def generate_mapping_information(num_clients: int) -> tuple[nx.MultiDiGraph, int, list]:
def generate_mapping_information(num_clients: int) -> tuple[nx.MultiDiGraph, int, list, list]:
"""Return ``nx.MultiDiGraph`` with client demand, depot id in graph, client ids in graph.
Args:
Expand All @@ -58,6 +65,7 @@ def generate_mapping_information(num_clients: int) -> tuple[nx.MultiDiGraph, int
map_network: ``nx.MultiDiGraph`` where nodes and edges represent locations and routes.
depot_id: Node ID of the depot location.
client_subset: List of client IDs in the map's graph.
map_bounds: List of lower and upper bound locations for map
"""
random.seed(num_clients)

Expand Down Expand Up @@ -85,7 +93,11 @@ def generate_mapping_information(num_clients: int) -> tuple[nx.MultiDiGraph, int
+ map_network.nodes[node_id]["demand_other"]
)

return map_network, depot_id, client_subset
# Get min and max coordinates to determine map bounds
coordinates = _get_coordinates(node_index_map)
map_bounds = [coordinates.min(0).tolist(), coordinates.max(0).tolist()]

return map_network, depot_id, client_subset, map_bounds


def _get_nodes(G: nx.Graph, force_id: int) -> tuple[folium.CustomIcon, list[int]]:
Expand All @@ -96,7 +108,7 @@ def _get_nodes(G: nx.Graph, force_id: int) -> tuple[folium.CustomIcon, list[int]


def show_locations_on_initial_map(
G: nx.MultiDiGraph, depot_id: int, client_subset: list
G: nx.MultiDiGraph, depot_id: int, client_subset: list, map_bounds: list[list]
) -> folium.Map:
"""Prepare map to be rendered initially on app screen.
Expand All @@ -109,12 +121,14 @@ def show_locations_on_initial_map(
folium.Map: Map with depot, client locations and tooltip popups.
"""
# create folium map on which to plot depots
tiles = "cartodb positron"
tiles = "cartodb positron" # foilum map theme

folium_map = ox.graph_to_gdfs(G, nodes=False, node_geometry=False).explore(
style_kwds={"opacity": 0.0}, tiles=tiles
)

folium_map.fit_bounds(map_bounds)

# add marker to the depot location
folium.Marker(
location=(G.nodes[depot_id]["y"], G.nodes[depot_id]["x"]),
Expand Down

0 comments on commit 1726516

Please sign in to comment.