from geoplotlib.layers import BaseLayer from geoplotlib.core import BatchPainter import geoplotlib from geoplotlib.colors import create_set_cmap from geoplotlib.utils import BoundingBox, epoch_to_str import time as ptime import pandas as pd import numpy as np from de2.routing import get_graph, euler_distance, pathfinder, find_percentage_point_in_path as fpp from twin.elements import Vessel class PoABLayer(BaseLayer): """ Renders the map. """ __colors__ = { "highlight": [150, 0, 250, 200], # yellow "nodes": [150, 0, 250, 200], # purple "ecological": [0, 255, 0, 200] # green } FPS = 30 def __init__(self, client): self.client = client self.berths = pd.read_csv("berths.csv") self.painter = BatchPainter() self.ticks = 0 self.time = 0 self.initial_time = 0 self.last_rt_time = ptime.time() self.time_factor = 1.0 self.vessels = [] self.graph = get_graph() def update_vessels(self): self.client.spin() if len(self.client.received["initial_time"]) > 0: self.initial_time, self.time_factor = eval(self.client.received["initial_time"].pop(0)) while len(self.client.received["vessels"]) > 0: vessel = eval(self.client.received["vessels"].pop(0)) # self.time_factor = (vessel[0] - self.time) / (ptime.time() - self.last_rt_time) self.time = vessel[0] self.ticks = 0 vdict = { "mmsi": vessel[1], "name": vessel[2], "task": vessel[3], "velocity": vessel[4], "source": vessel[5], "target": vessel[6], "start": vessel[7], "ETA": vessel[8], } self.vessels.append(vdict) self.last_rt_time = ptime.time() def draw_shape(self, x, y): """ Draws the shape on the layer, at a specific location. Args: x (numeric): The x-position to draw this shape at. y (numeric): The y-position to draw this shape at. """ self.painter.points(x, y, 5) def draw_path(self, path, proj): """ Draws a WSG84 path on the layer. Args: path: The path to draw as (x, y) tuples. proj: The projector. """ x = [] y = [] for ix in range(len(path) - 1): point1 = path[ix] point2 = path[ix + 1] x.append(point1[0]) x.append(point2[0]) y.append(point1[1]) y.append(point2[1]) px, py = proj.lonlat_to_screen(np.asarray(x), np.asarray(y)) vx1 = [e for ei, e in enumerate(px) if ei % 2 == 0] vx2 = [e for ei, e in enumerate(px) if ei % 2 == 1] vy1 = [e for ei, e in enumerate(py) if ei % 2 == 0] vy2 = [e for ei, e in enumerate(py) if ei % 2 == 1] self.painter.lines(vx1, vy1, vx2, vy2, width=1.5) def draw(self, proj, mouse_x, mouse_y, ui_manager): self.ticks += 1 self.update_vessels() if len(self.client.received["error"]) > 0: print('\n'.join(self.client.received["error"])) ui_manager.status('\n'.join(self.client.received["error"])) self.client.received["error"].clear() self.painter = BatchPainter() tooltips = [] # Show the simulation time in top right ts = self.time + (self.ticks / self.FPS) * self.time_factor ui_manager.info("TIME: %s (x %.1f)" % (epoch_to_str(self.initial_time + ts), self.time_factor)) # Get all sailing vessels rem = [] for vix, vessel in enumerate(self.vessels): col = self.__colors__["ecological"] eco = Vessel.ecological() dist = 0 if vessel["velocity"] < eco[0]: dist = eco[0] - vessel["velocity"] elif vessel["velocity"] > eco[1]: dist = vessel["velocity"] - eco[1] col[0] = min(255, int(dist) * 50) col[1] = 255 - col[0] self.painter.set_color(col) # Obtain (and draw) vessel trajectory if "trajectory" not in vessel: vessel["trajectory"] = pathfinder(self.graph, vessel["source"], vessel["target"]) path, dists = vessel["trajectory"] # Compute distance traveled and draw a vessel there # TODO: use distance_left when it should be updated tot_distance = sum(dists) traveled = (ts - vessel["start"]) * vessel["velocity"] distance_left = tot_distance - traveled if tot_distance == 0: p1 = p2 = path[0] percentage = 1. else: percentage = traveled / tot_distance p1, p2, percentage = fpp(path, dists, percentage) if percentage < 1: self.draw_path(path, proj) sx, sy = proj.lonlat_to_screen(np.asarray([p1[0]]), np.asarray([p1[1]])) tx, ty = proj.lonlat_to_screen(np.asarray([p2[0]]), np.asarray([p2[1]])) dx = (tx - sx) * percentage dy = (ty - sy) * percentage if euler_distance(sx + dx, sy + dy, mouse_x, mouse_y) < 10: self.painter.set_color(self.__colors__["highlight"]) tooltips.append("%s (MMSI: %s)" % (str(vessel["name"]), str(vessel["mmsi"]))) tooltips.append("task: %s | ETA: %s" % (vessel["task"], epoch_to_str(vessel["ETA"]))) tooltips.append("velocity: %.3f m/s" % vessel["velocity"]) tooltips.append("dist left/total: %.3f/%.3f m" % (distance_left, tot_distance)) # self.draw_path(path, proj) self.draw_shape(sx + dx, sy + dy) else: rem.append(vix) for vix in reversed(rem): self.vessels.pop(vix) ui_manager.tooltip("\n".join(tooltips)) self.painter.batch_draw() def bbox(self): box = [max(self.berths["center_lat"]), max(self.berths["center_lon"]), min(self.berths["center_lat"]), min(self.berths["center_lon"])] return BoundingBox(north=box[0], east=box[1], south=box[2], west=box[3]) if __name__ == '__main__': from networking.mqtt import MQTTClient client = MQTTClient("Dashboard") client.subscribe("initial_time") client.subscribe("vessels") client.subscribe("error") layer = PoABLayer(client) geoplotlib.set_window_size(640, 760) geoplotlib.tiles_provider({ 'url': lambda zoom, xtile, ytile: 'https://tile.openstreetmap.org/%d/%d/%d.png' % (zoom, xtile, ytile), 'tiles_dir': 'mytiles', 'attribution': 'Map tiles by OpenStreetMap Carto, under CC BY 3.0. Data @ OpenStreetMap contributors.' }) geoplotlib.add_layer(layer) geoplotlib.show()