class TrailsLayer(BaseLayer): def __init__(self): self.data = read_csv('SLOCs.csv') self.cmap = colorbrewer(self.data['name'], alpha=220) self.t = self.data['timestamp'].min() self.painter = BatchPainter() def draw(self, proj, mouse_x, mouse_y, ui_manager): self.painter = BatchPainter() df = self.data.where((self.data['timestamp'] > self.t) & (self.data['timestamp'] <= self.t + 24*3600)) #set minimum time interval for name in set(df['name']): grp = df.where(df['name'] == name) self.painter.set_color('blue') #set color, default: self.cmap[name] x0, y0 = proj.lonlat_to_screen(grp['Slon'], grp['Slat']) x1, y1 = proj.lonlat_to_screen(grp['Flon'], grp['Flat']) self.painter.points(x0, y0, 3) self.painter.lines(x0,y0,x1,y1,width=3) self.t += 24*3600 #set animation step size if self.t > self.data['timestamp'].max(): self.t = self.data['timestamp'].min() self.painter.batch_draw() ui_manager.info(epoch_to_str(self.t)) def bbox(self): return BoundingBox(north=9, west=110, south=1, east=95) #set boundingbox
class GraphLayer(BaseLayer): def __init__(self, data, src_lat, src_lon, dest_lat, dest_lon, linewidth=1, alpha=220, color='hot'): """Create a graph drawing a line between each pair of (src_lat, src_lon) and (dest_lat, dest_lon) :param data: data access object :param src_lat: field name of source latitude :param src_lon: field name of source longitude :param dest_lat: field name of destination latitude :param dest_lon: field name of destination longitude :param linewidth: line width :param alpha: color alpha :param color: color or colormap """ self.data = data self.src_lon = src_lon self.src_lat = src_lat self.dest_lon = dest_lon self.dest_lat = dest_lat self.linewidth = linewidth alpha = alpha self.color = color if type(self.color) == str: self.cmap = colors.ColorMap(self.color, alpha) def invalidate(self, proj): self.painter = BatchPainter() x0, y0 = proj.lonlat_to_screen(self.data[self.src_lon], self.data[self.src_lat]) x1, y1 = proj.lonlat_to_screen(self.data[self.dest_lon], self.data[self.dest_lat]) if type(self.color) == list: self.painter.set_color(self.color) self.painter.lines(x0, y0, x1, y1, width=self.linewidth) else: manhattan = np.abs(x0-x1) + np.abs(y0-y1) vmax = manhattan.max() distances = np.logspace(0, log10(manhattan.max()), 20) for i in range(len(distances)-1, 1, -1): mask = (manhattan > distances[i-1]) & (manhattan <= distances[i]) self.painter.set_color(self.cmap.to_color(distances[i], vmax, 'log')) self.painter.lines(x0[mask], y0[mask], x1[mask], y1[mask], width=self.linewidth) def draw(self, proj, mouse_x, mouse_y, ui_manager): self.painter.batch_draw() def bbox(self): return BoundingBox.from_points(lons=np.hstack([self.data[self.src_lon], self.data[self.dest_lon]]), lats=np.hstack([self.data[self.src_lat], self.data[self.dest_lat]]))
class DelaunayLayer(BaseLayer): def __init__(self, data, line_color=None, line_width=2, cmap=None, max_lenght=100): """ Draw a delaunay triangulation of the points :param data: data access object :param line_color: line color :param line_width: line width :param cmap: color map :param max_lenght: scaling constant for coloring the edges """ self.data = data if cmap is None and line_color is None: raise Exception('need either cmap or line_color') if cmap is not None: cmap = colors.ColorMap(cmap, alpha=196) self.cmap = cmap self.line_color = line_color self.line_width = line_width self.max_lenght = max_lenght @staticmethod def _get_area(p): x1, y1, x2, y2, x3, y3 = p return 0.5 * (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) def invalidate(self, proj): try: from scipy.spatial.qhull import Delaunay except ImportError: print('DelaunayLayer needs scipy >= 0.12') raise self.painter = BatchPainter() x, y = proj.lonlat_to_screen(self.data['lon'], self.data['lat']) points = list(set(zip(x, y))) dela = Delaunay(points) edges = set() for tria in dela.vertices: edges.add((tria[0], tria[1])) edges.add((tria[1], tria[2])) edges.add((tria[2], tria[0])) allx0 = [] ally0 = [] allx1 = [] ally1 = [] colors = [] for a, b in edges: x0, y0 = dela.points[a] x1, y1 = dela.points[b] allx0.append(x0) ally0.append(y0) allx1.append(x1) ally1.append(y1) if self.line_color: colors.append(self.line_color) colors.append(self.line_color) elif self.cmap: l = math.sqrt((x0 - x1)**2 + (y0 - y1)**2) c = self.cmap.to_color(l, self.max_lenght, 'log') colors.append(c) colors.append(c) self.painter.lines(allx0, ally0, allx1, ally1, colors, width=self.line_width) def draw(self, proj, mouse_x, mouse_y, ui_manager): self.painter.batch_draw() def bbox(self): return BoundingBox.from_points(lons=self.data['lon'], lats=self.data['lat'])
class GraphLayer(BaseLayer): def __init__(self, data, src_lat, src_lon, dest_lat, dest_lon, linewidth=1, alpha=220, color='hot'): """Create a graph drawing a line between each pair of (src_lat, src_lon) and (dest_lat, dest_lon) :param data: data access object :param src_lat: field name of source latitude :param src_lon: field name of source longitude :param dest_lat: field name of destination latitude :param dest_lon: field name of destination longitude :param linewidth: line width :param alpha: color alpha :param color: color or colormap """ self.data = data self.src_lon = src_lon self.src_lat = src_lat self.dest_lon = dest_lon self.dest_lat = dest_lat self.linewidth = linewidth alpha = alpha self.color = color if type(self.color) == str: self.cmap = colors.ColorMap(self.color, alpha) def invalidate(self, proj): self.painter = BatchPainter() x0, y0 = proj.lonlat_to_screen(self.data[self.src_lon], self.data[self.src_lat]) x1, y1 = proj.lonlat_to_screen(self.data[self.dest_lon], self.data[self.dest_lat]) if type(self.color) == list: self.painter.set_color(self.color) self.painter.lines(x0, y0, x1, y1, width=self.linewidth) else: manhattan = np.abs(x0 - x1) + np.abs(y0 - y1) vmax = manhattan.max() distances = np.logspace(0, log10(manhattan.max()), 20) for i in range(len(distances) - 1, 1, -1): mask = (manhattan > distances[i - 1]) & (manhattan <= distances[i]) self.painter.set_color( self.cmap.to_color(distances[i], vmax, 'log')) self.painter.lines(x0[mask], y0[mask], x1[mask], y1[mask], width=self.linewidth) def draw(self, proj, mouse_x, mouse_y, ui_manager): self.painter.batch_draw() def bbox(self): return BoundingBox.from_points( lons=np.hstack([self.data[self.src_lon], self.data[self.dest_lon]]), lats=np.hstack([self.data[self.src_lat], self.data[self.dest_lat]]))
class LineLayer(layers.BaseLayer): """ Draws line connecting points in hurricane path """ def __init__(self, data, num, color=None, point_size=2, linewidth=1, f_tooltip=None): """ Creates a LineLayer for the python geoplotlib library :param data: DataAccessObject of latitudes and longitudes :param num: number of data points in dat :param color: color of lines, just to red if none :param point_size: size of points :param linewidth: width of lines :param f_tooltip: an attribute of geoplotlib's BaseLayer that is not used """ self.data = data self.indexlst = num self.color = color if self.color is None: self.color = [255, 0, 0] self.point_size = point_size self.f_tooltip = f_tooltip self.linewidth = linewidth self.hotspots = HotspotManager() def invalidate(self, proj): """ This method is called each time layers need to be redrawn, i.e. on zoom. Typically in this method a BatchPainter is instantiated and all the rendering is performed :param proj: the current Projector object """ self.painter = BatchPainter() self.painter.set_color(self.color) x1, y1 = proj.lonlat_to_screen(self.data['lon'], self.data['lat']) self.painter.points(x1, y1, 2 * self.point_size, False) for i in self.indexlst: if i < len(self.data['lon']) - 1: x1 = self.data['lon'][i] y1 = self.data['lat'][i] x2 = self.data['lon'][i+1] y2 = self.data['lat'][i+1] x1, y1 = proj.lonlat_to_screen(x1, y1) x2, y2 = proj.lonlat_to_screen(x2, y2) self.painter.lines(x1, y1, x2, y2, width=self.linewidth) if self.f_tooltip: for i in range(0, len(x1)): record = {k: self.data[k][i] for k in self.data.keys()} self.hotspots.add_rect(x1[i] - self.point_size, y1[i] - self.point_size, 2 * self.point_size, 2 * self.point_size, self.f_tooltip(record)) def draw(self, proj, mouse_x, mouse_y, ui_manager): """ This method is called at every frame, and typically executes BatchPainter.batch_draw() :param proj: the current Projector object :param mouse_x: mouse x :param mouse_y: mouse y :param ui_manager: the current UiManager """ self.painter.batch_draw() picked = self.hotspots.pick(mouse_x, mouse_y) if picked: ui_manager.tooltip(picked) def bbox(self): """ Return the bounding box for this layer """ return BoundingBox.from_points(lons=self.data['lon'], lats=self.data['lat']) def on_key_release(self, key, modifiers): """ Override this method for custom handling of keystrokes :param key: the key that has been released :param modifiers: the key modifiers :return: True if the layer needs to call invalidate """ return False
class GraphLayer(BaseLayer): def __init__(self, data, src_lat, src_lon, dest_lat, dest_lon, linewidth=1, alpha=220, color='hot', levels=10, color_by=None, seg_scale='log'): """Create a graph drawing a line between each pair of (src_lat, src_lon) and (dest_lat, dest_lon) :param data: data access object :param src_lat: field name of source latitude :param src_lon: field name of source longitude :param dest_lat: field name of destination latitude :param dest_lon: field name of destination longitude :param linewidth: line width :param alpha: color alpha :param color: color or colormap :param levels: coloring levels :param color_by: attribute name for color, default using node distance :param seg_scale: coloring data segamentation sacle, 'log' or 'lin', 'lin' only used if not by distance """ self.data = data self.src_lon = src_lon self.src_lat = src_lat self.dest_lon = dest_lon self.dest_lat = dest_lat self.linewidth = linewidth alpha = alpha self.color = color if type(self.color) == str: self.cmap = colors.ColorMap(self.color, alpha, levels=levels) if color_by is None: self.color_by = 'distance' self.seg_scale = seg_scale def invalidate(self, proj): self.painter = BatchPainter() x0, y0 = proj.lonlat_to_screen(self.data[self.src_lon], self.data[self.src_lat]) x1, y1 = proj.lonlat_to_screen(self.data[self.dest_lon], self.data[self.dest_lat]) if type(self.color) == list: self.painter.set_color(self.color) self.painter.lines(x0, y0, x1, y1, width=self.linewidth) else: if self.color_by == 'distance': manhattan = np.abs(x0 - x1) + np.abs(y0 - y1) vmax = manhattan.max() segmentations = np.logspace(0, log10(vmax), 20) self.seg_scale = 'log' else: manhattan = self.data[self.color_by] vmax = manhattan.max() if self.seg_scale == 'log': # value 20 maybe should be optional segmentations = np.logspace(0, log10(vmax), 20) else: # linear segmentations = np.linspace(0, vmax, 20) for i in range(len(segmentations) - 1, 1, -1): mask = (manhattan > segmentations[i - 1]) & (manhattan <= segmentations[i]) self.painter.set_color( self.cmap.to_color(segmentations[i], vmax, self.seg_scale)) self.painter.lines(x0[mask], y0[mask], x1[mask], y1[mask], width=self.linewidth) def draw(self, proj, mouse_x, mouse_y, ui_manager): self.painter.batch_draw() def bbox(self): return BoundingBox.from_points( lons=np.hstack([self.data[self.src_lon], self.data[self.dest_lon]]), lats=np.hstack([self.data[self.src_lat], self.data[self.dest_lat]]))
class AnimatedProcess(BaseLayer): def __init__(self, network, edgelists, show_addresses=False, save_frames=False, line_width=1.3): # Set network and edge lists. self.network = network self.edgelists = edgelists self.num_frames = len(self.edgelists) # Set flag specifying whether to show addresses. self.show_addresses = show_addresses # Set flag specifying whether to save frames. self.save_frames = save_frames # Set line width. self.line_width = line_width # Initialize state counter. self.count = 0 # Initialize geolocator for retrieving addresses. geolocator = Nominatim(user_agent='test') # Set coordinates of nodes and get addresses. self.node_coordinates = np.empty((2, network.number_of_nodes()), dtype=float) self.node_addresses = [] for idx, node in enumerate(network.nodes()): self.node_coordinates[:, idx] = np.array(self.network.nodes[node]['latlon']) if self.show_addresses: address = geolocator.reverse(self.node_coordinates[:, idx]).address self.node_addresses.append(address[:address.index(',', address.index(',') + 1)]) def invalidate(self, proj): self.x, self.y = proj.lonlat_to_screen(self.node_coordinates[1, :], self.node_coordinates[0, :]) def draw(self, proj, mouse_x, mouse_y, ui_manager): # Prepare edges for next frame. self.edge_src = np.empty((2, len(self.edgelists[self.count])), dtype=float) self.edge_dst = np.empty((2, len(self.edgelists[self.count])), dtype=float) for idx, edge in enumerate(self.edgelists[self.count]): self.edge_src[:, idx] = self.network.nodes[edge[0]]['latlon'] self.edge_dst[:, idx] = self.network.nodes[edge[1]]['latlon'] self.edge_src_trans_x, self.edge_src_trans_y = proj.lonlat_to_screen(self.edge_src[1, :], self.edge_src[0, :]) self.edge_dst_trans_x, self.edge_dst_trans_y = proj.lonlat_to_screen(self.edge_dst[1, :], self.edge_dst[0, :]) # Initialize painter, plot nodes and addresses. self.painter = BatchPainter() self.painter.points(self.x, self.y, point_size=10, rounded=True) if self.show_addresses: self.painter.labels(self.x, self.y, self.node_addresses, font_size=10, anchor_x='left') # Plot edges. self.painter.set_color([255, 0, 0]) self.painter.lines(self.edge_src_trans_x, self.edge_src_trans_y, self.edge_dst_trans_x, self.edge_dst_trans_y, width=self.line_width) # Draw and increment counter. self.painter.batch_draw() if self.count < len(self.edgelists) - 1: self.count += 1 self.count = self.count % self.num_frames print("count: {0}/{1}".format(self.count, self.num_frames)) # If saving frames. if self.save_frames: GeoplotlibApp.screenshot(f'./results/animation_frames/{self.count}.png')
class DelaunayLayer(BaseLayer): def __init__(self, data, line_color=None, line_width=2, cmap=None, max_lenght=100): """ Draw a delaunay triangulation of the points :param data: data access object :param line_color: line color :param line_width: line width :param cmap: color map :param max_lenght: scaling constant for coloring the edges """ self.data = data if cmap is None and line_color is None: raise Exception('need either cmap or line_color') if cmap is not None: cmap = colors.ColorMap(cmap, alpha=196) self.cmap = cmap self.line_color = line_color self.line_width = line_width self.max_lenght = max_lenght @staticmethod def _get_area(p): x1, y1, x2, y2, x3, y3 = p return 0.5*(x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2)) def invalidate(self, proj): try: from scipy.spatial.qhull import Delaunay except ImportError: print('DelaunayLayer needs scipy >= 0.12') raise self.painter = BatchPainter() x, y = proj.lonlat_to_screen(self.data['lon'], self.data['lat']) points = list(set(zip(x,y))) dela = Delaunay(points) edges = set() for tria in dela.vertices: edges.add((tria[0], tria[1])) edges.add((tria[1], tria[2])) edges.add((tria[2], tria[0])) allx0 = [] ally0 = [] allx1 = [] ally1 = [] colors = [] for a, b in edges: x0, y0 = dela.points[a] x1, y1 = dela.points[b] allx0.append(x0) ally0.append(y0) allx1.append(x1) ally1.append(y1) if self.line_color: colors.append(self.line_color) colors.append(self.line_color) elif self.cmap: l = math.sqrt((x0 - x1)**2+(y0 - y1)**2) c = self.cmap.to_color(l, self.max_lenght, 'log') colors.append(c) colors.append(c) self.painter.lines(allx0, ally0, allx1, ally1, colors, width=self.line_width) def draw(self, proj, mouse_x, mouse_y, ui_manager): self.painter.batch_draw() def bbox(self): return BoundingBox.from_points(lons=self.data['lon'], lats=self.data['lat'])