mapper.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. from geoplotlib.layers import BaseLayer
  2. from geoplotlib.core import BatchPainter
  3. import geoplotlib
  4. from geoplotlib.utils import BoundingBox, epoch_to_str
  5. import pyglet
  6. import pandas as pd
  7. import numpy as np
  8. from de2.routing import get_graph, euler_distance, pathfinder, find_percentage_point_in_path as fpp
  9. class PoABLayer(BaseLayer):
  10. """
  11. Renders the map.
  12. """
  13. __colors__ = {
  14. "highlight": [255, 255, 0, 100], # yellow
  15. "nodes": [150, 0, 250, 200], # purple
  16. "sailing": [0, 150, 250, 200], # blue
  17. "tugging": [150, 250, 0, 200] # green
  18. }
  19. def __init__(self, sim=None, streamer=None):
  20. self.sim = sim
  21. self.streamer = streamer
  22. self.sim_active = False
  23. self.berths = pd.read_csv("berths.csv")
  24. # Uncomment the following lines if the berths list should only look at berths used
  25. # self.berths["used"] = False
  26. # self.paths = pd.read_csv("2022.csv")
  27. # self.paths.drop_duplicates(["Locatie van", "Locatie naar"], inplace=True)
  28. #
  29. # for _, traj in self.paths.iterrows():
  30. # self.berths.loc[self.berths["lpl_id"] == traj["Locatie van"], "used"] = True
  31. # self.berths.loc[self.berths["lpl_id"] == traj["Locatie naar"], "used"] = True
  32. # self.berths = self.berths[self.berths["used"]]
  33. # self.paths = []
  34. self.painter = BatchPainter()
  35. self.vcache = {} # vessel -> source
  36. self.pcache = {} # vessel -> path, dists
  37. self.tcache = {} # mmsi -> start time
  38. self.graph = get_graph()
  39. def draw_shape(self, x, y):
  40. """
  41. Draws the shape on the layer, at a specific location.
  42. Args:
  43. x (numeric): The x-position to draw this shape at.
  44. y (numeric): The y-position to draw this shape at.
  45. """
  46. self.painter.points(x, y, 5)
  47. def draw_path(self, path, proj):
  48. """
  49. Draws a WSG84 path on the layer.
  50. Args:
  51. path: The path to draw as (x, y) tuples.
  52. proj: The projector.
  53. """
  54. x = []
  55. y = []
  56. for ix in range(len(path) - 1):
  57. point1 = path[ix]
  58. point2 = path[ix + 1]
  59. x.append(point1[0])
  60. x.append(point2[0])
  61. y.append(point1[1])
  62. y.append(point2[1])
  63. px, py = proj.lonlat_to_screen(np.asarray(x), np.asarray(y))
  64. vx1 = [e for ei, e in enumerate(px) if ei % 2 == 0]
  65. vx2 = [e for ei, e in enumerate(px) if ei % 2 == 1]
  66. vy1 = [e for ei, e in enumerate(py) if ei % 2 == 0]
  67. vy2 = [e for ei, e in enumerate(py) if ei % 2 == 1]
  68. self.painter.lines(vx1, vy1, vx2, vy2, width=1.5)
  69. def draw(self, proj, mouse_x, mouse_y, ui_manager):
  70. self.painter = BatchPainter()
  71. tooltips = []
  72. # self.draw_path([[4.295200916909143, 51.33976669811829], [4.295765234634749, 51.33948916458878], [4.293551595412932, 51.33772327775724], [4.292987285902909, 51.338000801857774], [4.294133417960714, 51.33891513667706], [4.295200916909143, 51.33976669811829]], proj)
  73. # pts = [(4.29508,51.3404), (4.29472,51.33883), (4.29646,51.33891), (4.297,51.33805), (4.29754,51.33719), (4.29533,51.33968), (4.29813,51.33624), (4.29533,51.33968), (4.29867,51.33538), (4.29893,51.33495), (4.2992,51.33452), (4.29947,51.33409), (4.2953,51.33949), (4.29509,51.33908), (4.29518,51.33883), (4.29475,51.33873), (4.30082,51.33193), (4.29465,51.3387), (4.29473,51.33879), (4.2947,51.33877), (4.29469,51.33877), (4.29468,51.33877), (4.29468,51.33876), (4.29467,51.33876), (4.29467,51.33876), (4.29475,51.33879), (4.29474,51.33879), (4.29474,51.33879), (4.29467,51.33873), (4.29467,51.33872), (4.29467,51.33871), (4.2947,51.33874), (4.2947,51.33874), (4.2947,51.33874), (4.29469,51.33874), (4.29469,51.33874), (4.29469,51.33874), (4.29616,51.33904)]
  74. # self.draw_path(pts, proj)
  75. # self.painter.set_color([255, 0, 0, 200])
  76. #
  77. # # DRAW THE PoAB GRAPH
  78. # for v in self.graph.vs:
  79. # x, y = proj.lonlat_to_screen(np.asarray([v["x"]]), np.asarray([v["y"]]))
  80. # self.draw_shape(x, y)
  81. # for e in self.graph.es:
  82. # src = e.source_vertex
  83. # tgt = e.target_vertex
  84. # cx1, cy1 = proj.lonlat_to_screen(np.asarray([src["x"]]), np.asarray([src["y"]]))
  85. # cx2, cy2 = proj.lonlat_to_screen(np.asarray([tgt["x"]]), np.asarray([tgt["y"]]))
  86. # self.painter.lines(cx1, cy1, cx2, cy2, width=2)
  87. if not self.sim_active:
  88. ui_manager.status("Press Space to Start Simulation")
  89. else:
  90. # Show the simulation time in top right
  91. # model = self.sim.model
  92. time = self.streamer.time
  93. ts = int(time + self.streamer.starting_time)
  94. # time = model.clock.state["time"]
  95. # ts = int(time + model.scheduler.starting_time / 1000)
  96. ui_manager.info("TIME: %s" % epoch_to_str(ts))
  97. # Get all sailing vessels
  98. for vessel in self.streamer.vessels:
  99. if vessel.source is not None or vessel.target is not None:
  100. if vessel.task == "sailing":
  101. self.painter.set_color(self.__colors__["sailing"])
  102. else:
  103. self.painter.set_color(self.__colors__["tugging"])
  104. # Obtain (and draw) vessel trajectory
  105. if vessel.mmsi not in self.vcache or self.vcache[vessel.mmsi] != vessel.source:
  106. self.vcache[vessel.mmsi] = vessel.source
  107. self.pcache[vessel.mmsi] = pathfinder(self.graph, vessel.source, vessel.target)
  108. self.tcache[vessel.mmsi] = time
  109. path, dists = self.pcache[vessel.mmsi]
  110. self.draw_path(path, proj)
  111. # Compute distance traveled and draw a vessel there
  112. tot_distance = vessel.total_distance
  113. traveled_approx = tot_distance - vessel.distance_left
  114. delta = time - self.tcache[vessel.mmsi]
  115. traveled_approx += delta * vessel.velocity
  116. percentage = traveled_approx / tot_distance
  117. if sum(dists) == 0:
  118. p1 = p2 = path[0]
  119. percentage = 1.
  120. else:
  121. p1, p2, percentage = fpp(path, dists, percentage)
  122. sx, sy = proj.lonlat_to_screen(np.asarray([p1[0]]), np.asarray([p1[1]]))
  123. tx, ty = proj.lonlat_to_screen(np.asarray([p2[0]]), np.asarray([p2[1]]))
  124. dx = (tx - sx) * percentage
  125. dy = (ty - sy) * percentage
  126. if euler_distance(sx + dx, sy + dy, mouse_x, mouse_y) < 10:
  127. self.painter.set_color(self.__colors__["highlight"])
  128. tooltips.append("%s (MMSI: %s)" % (str(vessel.name), str(vessel.mmsi)))
  129. tooltips.append("%.3f m/s" % vessel.velocity)
  130. self.draw_shape(sx + dx, sy + dy)
  131. ui_manager.tooltip("\n".join(tooltips))
  132. self.painter.batch_draw()
  133. def bbox(self):
  134. box = [max(self.berths["center_lat"]), max(self.berths["center_lon"]), min(self.berths["center_lat"]), min(self.berths["center_lon"])]
  135. return BoundingBox(north=box[0], east=box[1], south=box[2], west=box[3])
  136. def on_key_release(self, key, modifiers):
  137. if key == pyglet.window.key.SPACE:
  138. if self.sim is not None:
  139. self.sim.simulate()
  140. self.sim_active = True
  141. if __name__ == '__main__':
  142. from de2.elements import Port
  143. from pypdevs.simulator import Simulator
  144. from de2.tracer import Streamer
  145. port = Port("PoAB", "results-de2/plan.csv")
  146. streamer = Streamer()
  147. sim = Simulator(port)
  148. # sim.setClassicDEVS()
  149. # sim.setVerbose(None)
  150. sim.setRealTime(scale=0.0002)
  151. sim.setCustomTracer("de2.tracer", "TracerPort", [streamer])
  152. # sim.setTerminationTime(31 * 24 * 60 * 60) # 14 days
  153. sim.setTerminationCondition(lambda _, m: m.scheduler.state["index"] >= len(m.scheduler.ivef))
  154. # sim.simulate()
  155. layer = PoABLayer(sim, streamer)
  156. geoplotlib.set_window_size(640, 760)
  157. geoplotlib.tiles_provider({
  158. 'url': lambda zoom, xtile, ytile: 'https://tile.openstreetmap.org/%d/%d/%d.png' % (zoom, xtile, ytile),
  159. 'tiles_dir': 'mytiles',
  160. 'attribution': 'Map tiles by OpenStreetMap Carto, under CC BY 3.0. Data @ OpenStreetMap contributors.'
  161. })
  162. geoplotlib.add_layer(layer)
  163. geoplotlib.show()
  164. sim.model.print_statistics()