-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvisual.py
306 lines (212 loc) · 8.27 KB
/
visual.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
import searoute as sr
import folium
import folium.plugins as plugins
from datetime import datetime,timedelta
class Visual():
def __init__(self, map):
self.map = map
self.features = []
self.time = []
self.routes = {}
self.ports = {}
self.ships ={}
self.output_path= "test"
self.date = datetime(2017, 6, 1)
def add_port(self, port):
# Añadir Puertos dado el diccionario
self.ports[port["id"]]=port
def add_route(self, route):
"""
Esta funciion carga la info de las rutas a la clase
ademas dada las coordenadas de sus respectivos puertos
calcula la ruta mas corta con la funcion get_shortest_path
Input:
route -> Diccionario con la informacion de rutas
Output: None
"""
id = route["id"]
ruta = {}
origin,destination = self.get_locations(route)
path = self.get_shortest_path(origin,destination)
ruta = {"port_1":route["puerto_1"],"port_2":route["puerto_2"],"path":path}
self.routes[id]=ruta
def add_ship(self, ship):
"""
Se añaden la info de los barcos a la simulacion
Ademas vemos cuales son los lugares en donde estara
el barco en la simulacion guardadas en
la variable locations
Input:
Output:
"""
id_route = ship["route"]
index = []
self.ships[ship["id"]] = ship
# Obtenemos la ruta donde esta el barco
path = self.routes[id_route]["path"]
for progress in ship["progress"]:
index.append(int(progress*len(path)))
locations = [path[i] for i in index]
locations= [[lon, lat] for lat, lon in locations]
# Enviamos los lugares donde estara el barco en la simulacion
# a la funcion feature
self.add_feature(locations)
def add_time(self, time):
"""
Se maneja el tema del tiempo, esta hecho para trabajar en dias
pero se puede modificar y generalizar
Al final se agrega a una lista self.time el dia particular al cual
corresponde el timepo discreto
Input:
time -> Numero de tiempos de la simulacion
Output: None
"""
for t in range(0, time):
current_date = self.date + timedelta(days=t)
self.time.append(current_date.strftime("%Y-%m-%dT00:00:00"))
def get_locations(self, route):
"""
Input:
route -> Diccionario con info de una ruta
Output:
origin -> coordenadas de un puerto
destination -> coordenadas de un puerto
Funcion que retorna las coordenadas de los puertos asociadas
a una ruta de interes
"""
p1 = route['puerto_1']
p2 = route['puerto_2']
origin = self.ports[p1]["location"]
destination = self.ports[p2]["location"]
return origin, destination
def get_shortest_path(self, origin, destination):
"""
Funcion que retorna una lista de puntos de la ruta mas corta
Input:
origin -> Coordenada
destination -> Coordenada
Output:
route_folium -> Retorna una lista de coordenadas
"""
# Usando la libreria searoute obtenemos una lista de puntos de la ruta mas corta
# entre dos coordenadas
route = sr.searoute(origin[::-1], destination[::-1])
# dejamos la lista de puntos de la ruta mas corta en un formato tal que
# sea compatible con la libreria folium
route_folium= [sublista[::-1] for sublista in route["geometry"]["coordinates"]]
# añade al mapa inea que une los puntos de la ruta mas corta SIN MOVIMINETO
# folium.PolyLine(route_folium, tooltip="Coast").add_to(self.map)
# añade al mapa linea que une los puntos de la ruta mas corta CON MOVIMINETO
plugins.AntPath(reverse="False", locations = route_folium,dash_array=[20,30],color="blue" ).add_to(self.map)
# Centrar el zoom del mapa
self.map.fit_bounds(self.map.get_bounds())
return route_folium
def add_markers(self):
"""
Añade marcadores a entidades estaticas en el mapa
Input: None
Output: None
"""
# Añade marcadores a las entidades " estaticas " (las que no se muevan)
# osea a los puertos en este caso
for port in self.ports:
name = self.ports[port]["name"]
location = self.ports[port]["location"]
state = (self.ports[port]["state"])
if state!="True":
state = False
else:
state =True
color = "green" if state else "red"
icon = "anchor-circle-check" if state else "anchor-circle-xmark"
# añade el marcador/icono al mapa
folium.Marker(
location=location,
popup=f"Locacion:{location}\nCapacidad: 100 barcos",
tooltip=f"Puerto de {name}",
icon=folium.Icon(icon=icon, prefix="fa", color=color) # Icono de ancla con Font Awesome
).add_to(self.map)
def add_feature(self, info):
"""
Funcion que añade un objeto llamado feature (caracteristicas)
a la lista features, cada feature tiene la informacion de la posicion
de un barco en el tiempo, con su geometria, color, etc...
El formato de este objeto esta hecho de tal forma que sea reconocido
por la libreria TimestampedGeoJSON, asi se podra visualizar y manejar
el movimiento de los barcos durante el tiempo
Input: Info
Output: None
"""
# Necesario este objeto para usar TimestampedGeoJSON
feature= [{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": info,
},
"properties": {
"times": self.time,
"style": {
"color": "red",
"weight": 0,},},
}]
self.features.append(feature)
def run(self):
"""
Se agrega todas las caracteristicas finales al mapa
tales como marcadores y movimiento
Input : None
Output: None
"""
# Se añaden los marcadores al mapa
self.add_markers()
n = len(self.features)
# TimestampedGeoJSON necesita una suma de listas de las caracteristicas
features = sum(self.features[:n], [])
# Este plugin se encarga de mover el barco!
# Debemos añadirle ciertos atributos
plugins.TimestampedGeoJson(
{
"type": "FeatureCollection",
"features": features
},
period="P1D",
auto_play=False,
loop=False,
).add_to(self.map)
def save_map(self):
# Guarda el mapa en un archivo html
self.map.save(f"{self.output_path}.html")
def create_simulation(ships, ports, routes, time, type, output_path):
"""
Esta funcion se encarga de inicializar la clase visual
ejecutando sus funciones para guardar el archivo
que contiene la visualizacion en .html
Input:
ships -> Diccionario con informacion de los barcos
ports -> Diccionario con informacion de los puertos
routes -> Diccionario con informacion de los rutas
time -> Tiempo de la simulacion
output_path-> Nombre de la ruta donde se guardara el archivo
.html junto con su nombre
Output:
-> Sin output
"""
# Creamos el objeto mapa de la clase Map de folium
mapa = folium.Map(tiles=type, prefer_canvas=True)
# Inicializamos la clase Visual
visual = Visual(mapa)
# Añadimos la informacion de los agentes a la clase Visual
for port in ports:
visual.add_port(ports[port])
for route in routes:
visual.add_route(routes[route])
for ship in ships:
visual.add_ship(ships[ship])
# output path de la simulacion
visual.output_path = output_path
# Tiempos a simular
visual.add_time(time)
visual.run()
# Guardamos el mapa
visual.save_map()