eflips-eval 1.2.2__tar.gz → 1.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {eflips_eval-1.2.2 → eflips_eval-1.3.0}/PKG-INFO +5 -2
- {eflips_eval-1.2.2 → eflips_eval-1.3.0}/eflips/eval/input/prepare.py +93 -0
- {eflips_eval-1.2.2 → eflips_eval-1.3.0}/eflips/eval/input/visualize.py +54 -0
- {eflips_eval-1.2.2 → eflips_eval-1.3.0}/pyproject.toml +5 -2
- {eflips_eval-1.2.2 → eflips_eval-1.3.0}/LICENSE.md +0 -0
- {eflips_eval-1.2.2 → eflips_eval-1.3.0}/README.md +0 -0
- {eflips_eval-1.2.2 → eflips_eval-1.3.0}/eflips/eval/input/__init__.py +0 -0
- {eflips_eval-1.2.2 → eflips_eval-1.3.0}/eflips/eval/output/__init__.py +0 -0
- {eflips_eval-1.2.2 → eflips_eval-1.3.0}/eflips/eval/output/prepare.py +0 -0
- {eflips_eval-1.2.2 → eflips_eval-1.3.0}/eflips/eval/output/visualize.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: eflips-eval
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Ludger Heide
|
|
6
6
|
Author-email: ludger.heide@lhtechnologies.de
|
|
@@ -10,9 +10,12 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.11
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
12
|
Requires-Dist: dash-cytoscape (>=1.0.1,<2.0.0)
|
|
13
|
-
Requires-Dist: eflips-model (>=2.3.0,<
|
|
13
|
+
Requires-Dist: eflips-model (>=2.3.0,<5.0.0)
|
|
14
|
+
Requires-Dist: folium (>=0.17.0,<0.18.0)
|
|
14
15
|
Requires-Dist: pandas (>=2.1.4,<3.0.0)
|
|
15
16
|
Requires-Dist: plotly (>=5.19.0,<6.0.0)
|
|
17
|
+
Requires-Dist: seaborn (>=0.13.2,<0.14.0)
|
|
18
|
+
Requires-Dist: shapely (>=2.0.5,<3.0.0)
|
|
16
19
|
Description-Content-Type: text/markdown
|
|
17
20
|
|
|
18
21
|
[](https://github.com/mpm-tu-berlin/eflips-eval/actions/workflows/unittests.yml)
|
|
@@ -5,6 +5,7 @@ from typing import Dict, List
|
|
|
5
5
|
import pandas as pd
|
|
6
6
|
import sqlalchemy
|
|
7
7
|
from eflips.model import Rotation, Trip, Route
|
|
8
|
+
from shapely import wkb # type: ignore
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def rotation_info(
|
|
@@ -98,6 +99,98 @@ def rotation_info(
|
|
|
98
99
|
return df
|
|
99
100
|
|
|
100
101
|
|
|
102
|
+
def geographic_trip_plot(
|
|
103
|
+
scenario_id: int,
|
|
104
|
+
session: sqlalchemy.orm.session.Session,
|
|
105
|
+
rotation_ids: None | int | List[int] = None,
|
|
106
|
+
) -> pd.DataFrame:
|
|
107
|
+
"""
|
|
108
|
+
This function creates a dataframe that can be used to visualize the geographic distribution of rotations. It creates
|
|
109
|
+
a dataframe with one row for each trip and the following columns:
|
|
110
|
+
|
|
111
|
+
- rotation_id: the id of the rotation
|
|
112
|
+
- rotation_name: the name of the rotation
|
|
113
|
+
- vehicle_type_id: the id of the vehicle type
|
|
114
|
+
- vehicle_type_name: the name of the vehicle type
|
|
115
|
+
- originating_depot_id: the id of the originating depot
|
|
116
|
+
- originating_depot_name: the name of the originating depot
|
|
117
|
+
- distance: the distance of the route
|
|
118
|
+
- coordinates: An array of *(lon, lat)* tuples with the coordinates of the route - the shape if set, otherwise the stops
|
|
119
|
+
- line_name: the name of the line, which is the first part of the rotation name. Used for sorting
|
|
120
|
+
|
|
121
|
+
:param scenario_id:
|
|
122
|
+
:param session:
|
|
123
|
+
:param rotation_ids:
|
|
124
|
+
:return: a pandas DataFrame
|
|
125
|
+
"""
|
|
126
|
+
rotations_q = session.query(Rotation).filter(Rotation.scenario_id == scenario_id)
|
|
127
|
+
if rotation_ids is not None:
|
|
128
|
+
if isinstance(rotation_ids, int):
|
|
129
|
+
rotation_ids = [rotation_ids]
|
|
130
|
+
rotations_q = rotations_q.filter(Rotation.id.in_(rotation_ids))
|
|
131
|
+
rotations_q = rotations_q.options(
|
|
132
|
+
sqlalchemy.orm.joinedload(Rotation.trips)
|
|
133
|
+
.joinedload(Trip.route)
|
|
134
|
+
.joinedload(Route.line)
|
|
135
|
+
)
|
|
136
|
+
rotations_q = rotations_q.options(
|
|
137
|
+
sqlalchemy.orm.joinedload(Rotation.vehicle_type),
|
|
138
|
+
)
|
|
139
|
+
rotations_q = rotations_q.options(
|
|
140
|
+
sqlalchemy.orm.joinedload(Rotation.trips)
|
|
141
|
+
.joinedload(Trip.route)
|
|
142
|
+
.joinedload(Route.departure_station),
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
result: List[Dict[str, int | float | str | datetime]] = []
|
|
146
|
+
for rotation in rotations_q:
|
|
147
|
+
origin_depot_id = rotation.trips[0].route.departure_station_id
|
|
148
|
+
origin_depot_name = rotation.trips[0].route.departure_station.name
|
|
149
|
+
line_name = rotation.trips[0].route.line.name
|
|
150
|
+
vehicle_type_id = rotation.vehicle_type_id
|
|
151
|
+
vehicle_type_name = rotation.vehicle_type.name
|
|
152
|
+
|
|
153
|
+
for trip in rotation.trips:
|
|
154
|
+
# Obtain the coordinates of the route
|
|
155
|
+
if trip.route.geom is not None:
|
|
156
|
+
raise NotImplementedError(
|
|
157
|
+
"Geometries are not yet supported. Check if the code below 'just works'."
|
|
158
|
+
"If not, you need to implement the conversion to coordinates."
|
|
159
|
+
)
|
|
160
|
+
line_geom = wkb.loads(bytes(trip.route.geom.data))
|
|
161
|
+
line_coords = [(point.y, point.x) for point in line_geom.coords]
|
|
162
|
+
else:
|
|
163
|
+
line_coords = []
|
|
164
|
+
point_geom = wkb.loads(bytes(trip.route.departure_station.geom.data))
|
|
165
|
+
lon, lat = point_geom.x, point_geom.y
|
|
166
|
+
line_coords.append((lat, lon))
|
|
167
|
+
for assoc in trip.route.assoc_route_stations:
|
|
168
|
+
if assoc.location is not None:
|
|
169
|
+
station_coordinates = wkb.loads(bytes(assoc.location.data))
|
|
170
|
+
else:
|
|
171
|
+
station_coordinates = wkb.loads(bytes(assoc.station.geom.data))
|
|
172
|
+
lon, lat = station_coordinates.x, station_coordinates.y
|
|
173
|
+
line_coords.append((lat, lon))
|
|
174
|
+
point_geom = wkb.loads(bytes(trip.route.arrival_station.geom.data))
|
|
175
|
+
lon, lat = point_geom.x, point_geom.y
|
|
176
|
+
line_coords.append((lat, lon))
|
|
177
|
+
|
|
178
|
+
result.append(
|
|
179
|
+
{
|
|
180
|
+
"rotation_id": rotation.id,
|
|
181
|
+
"rotation_name": rotation.name,
|
|
182
|
+
"vehicle_type_id": vehicle_type_id,
|
|
183
|
+
"vehicle_type_name": vehicle_type_name,
|
|
184
|
+
"originating_depot_id": origin_depot_id,
|
|
185
|
+
"originating_depot_name": origin_depot_name,
|
|
186
|
+
"distance": trip.route.distance,
|
|
187
|
+
"coordinates": line_coords,
|
|
188
|
+
"line_name": line_name,
|
|
189
|
+
}
|
|
190
|
+
)
|
|
191
|
+
return pd.DataFrame(result)
|
|
192
|
+
|
|
193
|
+
|
|
101
194
|
def single_rotation_info(
|
|
102
195
|
rotation_id: int,
|
|
103
196
|
session: sqlalchemy.orm.session.Session,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import dash_cytoscape as cyto # type: ignore
|
|
2
|
+
import folium # type: ignore
|
|
2
3
|
import pandas as pd
|
|
3
4
|
import plotly.express as px # type: ignore
|
|
4
5
|
import plotly.graph_objs as go # type: ignore
|
|
6
|
+
import seaborn as sns # type: ignore
|
|
5
7
|
from eflips.model import TripType
|
|
6
8
|
|
|
7
9
|
|
|
@@ -55,6 +57,58 @@ def rotation_info(prepared_data: pd.DataFrame) -> go.Figure:
|
|
|
55
57
|
return fig
|
|
56
58
|
|
|
57
59
|
|
|
60
|
+
def geographic_trip_plot(prepared_data: pd.DataFrame) -> folium.Map:
|
|
61
|
+
"""
|
|
62
|
+
This function visualizes the trips on a map using folium. The trips are lines between the departure and arrival
|
|
63
|
+
stations.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
:param prepared_data: A Pandas dataframe with the following columns:
|
|
68
|
+
- rotation_id: the id of the rotation
|
|
69
|
+
- rotation_name: the name of the rotation
|
|
70
|
+
- vehicle_type_id: the id of the vehicle type
|
|
71
|
+
- vehicle_type_name: the name of the vehicle type
|
|
72
|
+
- originating_depot_id: the id of the originating depot
|
|
73
|
+
- originating_depot_name: the name of the originating depot
|
|
74
|
+
- distance: the distance of the route
|
|
75
|
+
- coordinates: An array of *(lon, lat)* tuples with the coordinates of the route - the shape if set, otherwise the stops
|
|
76
|
+
- line_name: the name of the line, which is the first part of the rotation name. Used for sorting
|
|
77
|
+
:return: A folium map object
|
|
78
|
+
"""
|
|
79
|
+
# Use seaborn to create a color palette for each originating depot
|
|
80
|
+
palette = sns.color_palette(
|
|
81
|
+
"husl", n_colors=len(prepared_data["originating_depot_id"].unique())
|
|
82
|
+
)
|
|
83
|
+
colors = {}
|
|
84
|
+
for depot_name in prepared_data["originating_depot_name"].unique():
|
|
85
|
+
color = palette.pop(0)
|
|
86
|
+
# Turn the color into a hex string
|
|
87
|
+
color = "#{:02x}{:02x}{:02x}".format(*[int(c * 255) for c in color])
|
|
88
|
+
colors[depot_name] = color
|
|
89
|
+
|
|
90
|
+
# Obtain the mean latitude and longitude for the map center
|
|
91
|
+
lat_center = (
|
|
92
|
+
prepared_data["coordinates"]
|
|
93
|
+
.apply(lambda x: sum([c[0] for c in x]) / len(x))
|
|
94
|
+
.mean()
|
|
95
|
+
)
|
|
96
|
+
lon_center = (
|
|
97
|
+
prepared_data["coordinates"]
|
|
98
|
+
.apply(lambda x: sum([c[1] for c in x]) / len(x))
|
|
99
|
+
.mean()
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
map = folium.Map(
|
|
103
|
+
location=[lat_center, lon_center], zoom_start=11, tiles="Cartodb dark_matter"
|
|
104
|
+
)
|
|
105
|
+
for i, row in prepared_data.iterrows():
|
|
106
|
+
color = colors[row["originating_depot_name"]]
|
|
107
|
+
pl = folium.PolyLine(row["coordinates"], color=color, weight=2.5, opacity=1)
|
|
108
|
+
map.add_child(pl)
|
|
109
|
+
return map
|
|
110
|
+
|
|
111
|
+
|
|
58
112
|
def single_rotation_info(prepared_data: pd.DataFrame) -> cyto.Cytoscape:
|
|
59
113
|
"""
|
|
60
114
|
This plots a single rotation as a network graph. The nodes are the stops and the edges are the trips between the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "eflips-eval"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.3.0"
|
|
4
4
|
description = ""
|
|
5
5
|
authors = [
|
|
6
6
|
"Ludger Heide <ludger.heide@lhtechnologies.de>",
|
|
@@ -13,10 +13,13 @@ include= ["LICENSE.md"]
|
|
|
13
13
|
|
|
14
14
|
[tool.poetry.dependencies]
|
|
15
15
|
python = "^3.10"
|
|
16
|
-
eflips-model = ">=2.3.0,<
|
|
16
|
+
eflips-model = ">=2.3.0,<5.0.0"
|
|
17
17
|
pandas = "^2.1.4"
|
|
18
18
|
plotly = "^5.19.0"
|
|
19
19
|
dash-cytoscape = "^1.0.1"
|
|
20
|
+
shapely = "^2.0.5"
|
|
21
|
+
folium = "^0.17.0"
|
|
22
|
+
seaborn = "^0.13.2"
|
|
20
23
|
|
|
21
24
|
[tool.poetry.group.dev.dependencies]
|
|
22
25
|
mypy = "^1.7.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|