Defining a basic simulation on a real graph#
Imports#
Import the required libraries
import opentnsim
print('This notebook has been tested with OpenTNSim version {}'.format(opentnsim.__version__))
This notebook has been tested with OpenTNSim version 1.3.7
# Used for mathematical functions
# package(s) related to time, space and id
import math
import datetime
import time
import platform
import random
import os
import pathlib
import warnings
# you need these dependencies (you can get these from anaconda)
# package(s) related to the simulation
import simpy
# spatial libraries
import pyproj
import shapely.geometry
from simplekml import Kml, Style
# package(s) for data handling
import numpy as np
import matplotlib.pyplot as plt
# OpenTNSIM
import opentnsim
import opentnsim.core as core
import opentnsim.utils
# Used for making the graph to visualize our problem
import networkx as nx
import pandas as pd
from shapely.errors import ShapelyDeprecationWarning
warnings.filterwarnings("ignore", category=ShapelyDeprecationWarning)
src_dir = pathlib.Path(opentnsim.__file__).parent.parent
# Graph location
location_graph = src_dir / "notebooks"
name_graph = location_graph / "Shape-Files" / "Rotterdam-Antwerpen-corridor" / "edges_2.shp"
Create graph#
We will now visualize the network based on the file edges_2.shp Important:
If you use windows and get the following error “ImportError: read_shp requires OGR: http://www.gdal.org/”, you probably have this issue. Solving it is possible by running the following commands in your terminal (as explained here:
#Create a new virtual environment
conda create -n testgdal -c conda-forge gdal vs2015_runtime=14
#Activate virtual environment
activate testgdal
#Open Jupyter notebook
jupyer notebook
# Read the shape-file
FG = opentnsim.utils.read_shp(str(name_graph), simplify=True)
# Draw the shape-file to get a first impression
plt.figure(figsize=(18,18))
nx.draw(FG)
# Show the drawing
plt.show()
# calculate distance between two points
def calculate_distance(orig, dest):
wgs84 = pyproj.Geod(ellps='WGS84')
orig = shapely.geometry.shape(orig)
dest = shapely.geometry.shape(dest)
distance = wgs84.inv(orig.x, orig.y,
dest.x, dest.y)[2]
return distance
H_G = nx.Graph()
lat_lon_to_index = {}
edge_id_counter = 0
for i, node in enumerate(FG.nodes(data = True)):
H_G.add_node(i, pos = node[0],
name = "Node {}".format(i),
geometry = shapely.geometry.Point(node[0][0], node[0][1]))
lat_lon_to_index[node[0]] = i
for edge in FG.edges(data=True):
H_G.add_edge(lat_lon_to_index[edge[0]],
lat_lon_to_index[edge[1]],
dis= calculate_distance(nx.get_node_attributes(H_G, 'geometry')[lat_lon_to_index[edge[1]]],
nx.get_node_attributes(H_G, 'geometry')[lat_lon_to_index[edge[0]]]))
FG = H_G.to_directed()
plt.figure(figsize=(18,18))
nx.draw(FG, nx.get_node_attributes(FG, 'pos'), with_labels=True, node_size=0.5, font_size = 2, width = 0.2, arrowsize = 3)
plt.show()
Create vessels#
Vessel without graph, but with shortest path.
# Make a class out of mix-ins
TransportResource = type(
'TransportResource',
(core.Identifiable, core.Movable,
core.HasResource, core.Routeable, core.HasContainer, core.ExtraMetadata), {})
# For testing purposes we only want v to be 1
def compute_v_provider(v_empty, v_full):
return lambda x: 1
data_vessel = {"env": None,
"name": "Vessel number 1",
"route": None,
"geometry": shapely.geometry.Point(4.0092314, 51.4598245), # lon, lat
"capacity": 1_000,
"v": 1}
# create the transport processing resource
vessel = TransportResource(**data_vessel)
/tmp/ipykernel_3953/3728699768.py:21: DeprecationWarning:
Call to deprecated class Routeable. (Use Routable instead of Routeable)
Define paths#
# select 2 nodes on the graph
source = 272
target = 373
path = nx.dijkstra_path(FG, source, target)
print(path)
[272, 209, 208, 210, 278, 277, 354, 353, 373]
Run simulation#
Now we run a simulation for the two constructed paths
def start(env, vessel):
while True:
vessel.log_entry("Start sailing", env.now, "", vessel.geometry)
yield from vessel.move()
vessel.log_entry("Stop sailing", env.now, "", vessel.geometry)
print(vessel.geometry)
if vessel.geometry == nx.get_node_attributes(FG, "geometry")[vessel.route[-1]]:
break
# Start simpy environment
simulation_start = datetime.datetime.now()
env = simpy.Environment(initial_time = time.mktime(simulation_start.timetuple()))
env.epoch = time.mktime(simulation_start.timetuple())
# Add graph to environment
env.FG = FG
# Add environment and path to the vessel
vessel.env = env
vessel.route = path
# Start the simulation
env.process(start(env, vessel))
env.run()
print("Simulation of path {} took {} seconds".format(path, int(env.now - env.epoch)))
POINT (4.0132076 51.4431779)
Simulation of path [272, 209, 208, 210, 278, 277, 354, 353, 373] took 1881 seconds
/tmp/ipykernel_3953/3716411016.py:3: DeprecationWarning:
Call to deprecated method log_entry. (Use .log_entry_v0 instead)
/tmp/ipykernel_3953/3716411016.py:5: DeprecationWarning:
Call to deprecated method log_entry. (Use .log_entry_v0 instead)
Output#
We can now show the output of the the vessel. It should sail from source to target in about half an hour.
pd.DataFrame(vessel.log)
Timestamp | |
---|---|
0 | 2024-05-30 09:34:56.000000 |
1 | 2024-05-30 09:34:56.000000 |
2 | 2024-05-30 09:40:16.482582 |
3 | 2024-05-30 09:40:16.482582 |
4 | 2024-05-30 09:45:04.857297 |
5 | 2024-05-30 09:45:04.857297 |
6 | 2024-05-30 09:47:06.671188 |
7 | 2024-05-30 09:47:06.671188 |
8 | 2024-05-30 09:54:24.512212 |
9 | 2024-05-30 09:54:24.512212 |
10 | 2024-05-30 09:57:56.913521 |
11 | 2024-05-30 09:57:56.913521 |
12 | 2024-05-30 09:59:56.492497 |
13 | 2024-05-30 09:59:56.492497 |
14 | 2024-05-30 10:04:23.837196 |
15 | 2024-05-30 10:04:23.837196 |
16 | 2024-05-30 10:06:17.867350 |
17 | 2024-05-30 10:06:17.867350 |