def astar_cnx(g, source_id=None, target_id=None, routes=None): from limic.util import locate_by_id, distance cs, node2closest, node2c = g.cs, g.node2closest, g.node2c if isinstance(source_id, tuple) and isinstance(target_id, tuple): source, target = source_id[0], target_id[0] else: nodes = list(node2closest.keys()) nodes.extend(node2c.keys()) ids = list(map(lambda x: x[0], nodes)) source, target = locate_by_id(ids, source_id, target_id) source, target = nodes[source], nodes[target] if not routes is None: routes.append((source, target)) if source not in node2closest: if target in node2closest or node2c[source] != node2c[target]: cost = dist = distance(source, target) path = None, [(0., 0., False) + source, (float('inf'), dist, True) + target] return path path = astar_nx(node2c[source], (source, ), (target, )) return path if target not in node2closest: cost = dist = distance(source, target) path = None, [(0., 0., False) + source, (float('inf'), dist, True) + target] return path paths = [] def extend(cp1, cp2): cost = cp1[0] path = cp1[1] assert (path[-1][3:] == cp2[1][0][3:]) path.extend(map(lambda x: ((x[0] + cost, ) + x[1:]), cp2[1][1:])) cost += cp2[0] return cost, path for ns, ps, cs in node2closest[source]: for nt, pt, ct in node2closest[target]: c = node2c[ns] if c != node2c[nt]: cost = dist = distance(source, target) path = None, [(0., 0., False) + source, (float('inf'), dist, True) + target] return path cp = astar_nx(ps, (source, ), (ns, )) cpc = astar_nx(c, (ns, ), (nt, )) for u, v in zip(cpc[1], cpc[1][1:]): u, v = u[3:], v[3:] cuv = c[u][v] sg = cuv["path"] cpuv = astar_nx(sg, (u, ), (v, )) cp = extend(cp, cpuv) cpt = astar_nx(pt, (nt, ), (target, )) cp = extend(cp, cpt) paths.append(cp) # paths.append((cs+c.graph['lengths'][ns][nt]+ct,[])) return min(paths)
def build_edges(g, find_all_neighbours, around, eps, safe_dist, penalize): from limic.overpass import pylon from limic.util import distance #count = 0 neighbours2intersection = {} minusid = [0] latlon2id = {} for tower in list(g.nodes()): #count += 1 #if verbosity >= 2: print(count,"of",total) for neighbour in find_all_neighbours(tower, around, eps, safe_dist, minusid, latlon2id, penalize): if neighbour.tower in g or neighbour.tower.id < 0: #if verbosity >= 2: print("adding",neighbour) g.add_edge(tower, pylon(neighbour.tower.id, neighbour.tower.latlon), weight=neighbour.dist, type=neighbour.air) if neighbour.tower.id < 0: for intersection in neighbours2intersection.setdefault( neighbour.tower.neighbours, []): if intersection.latlon != neighbour.tower.latlon: #if verbosity >= 2: print("double intersection:",intersection.latlon,neighbour.tower.latlon) g.add_edge(intersection, neighbour.tower, weight=distance(intersection.latlon, neighbour.tower.latlon), type=False) neighbours2intersection[neighbour.tower.neighbours].append( neighbour.tower)
def reconstruct_path_direct(current): from limic.util import distance dist = current.g - current.origin.g if current.origin else 0. air = distance( current.tower.latlon, current.origin.tower.latlon) < 0.9 * dist if current.origin else False cost = 0. path = [(current.g, dist, air, current.tower.id, current.tower.latlon[0], current.tower.latlon[1])] while current.origin: current = current.origin cost = current.g dist = cost - current.origin.g if current.origin else 0. air = distance( current.tower.latlon, current.origin.tower.latlon) < 0.9 * ( current.g - current.origin.g) if current.origin else False path.append((cost, dist, air, current.tower.id, current.tower.latlon[0], current.tower.latlon[1])) path.reverse() return cost, path
def nodes_in_geometry(tree, polygon): from limic.util import distance from shapely.geometry import Point, Polygon southwest, middle, _ = bounds_center(polygon) radius = distance(southwest, middle) candidates = tree.query(middle, radius) get_latlon = tree.get_latlon return [ candidate for candidate in candidates if Point(*get_latlon(candidate)).within(Polygon(polygon)) ]
def astar_nx(g, source_id=None, target_id=None): from limic.util import locate_by_id, haversine_distance from networkx import astar_path, NetworkXNoPath if isinstance(source_id, tuple) and isinstance(target_id, tuple): source, target = source_id[0], target_id[0] else: nodes = list(g.nodes()) ids = list(map(lambda x: x[0], nodes)) source, target = locate_by_id(ids, source_id, target_id) source, target = nodes[source], nodes[target] targetlat, targetlong = target[1], target[2] def distance(x, y): #assert(target==y) return haversine_distance(longx=x[2], latx=x[1], longy=targetlong, laty=targetlat) try: if 'paths' in g.graph and g.graph['paths']: path = g.graph['paths'][source][target] else: path = astar_path(g, source, target, heuristic=distance) cost = dist = 0.0 dpath = [(cost, dist, False) + path[0]] for i in range(1, len(path)): ggg = g[path[i - 1]][path[i]] dist = ggg['weight'] cost += dist air = ggg['type'] dpath.append((cost, dist, air < 0) + path[i]) return cost, dpath except NetworkXNoPath as e: cost = dist = distance(source, target) path = None, [(0., 0., False) + source, (float('inf'), dist, True) + target] return path
def astar_tower_direct(start, end, max_fly=1000, eps=0.01, safe_dist=100, penalty=20, prec=1): class node: def __init__(self, f, g, tower, origin): self.f = f self.g = g self.tower = tower self.origin = origin def __lt__(self, other): return self.f < other.f def __repr__(self): return "node(f=" + repr(self.f) + ",g=" + repr( self.g) + ",tower=" + repr(self.tower) + ",origin=" + repr( self.origin.tower.id if self.origin else self.origin) + ")" from limic.overpass import find_all_neighbours from heapq import heappop, heappush, heapify from limic.util import distance from math import inf latlon2id = {} minusid = [0] start_node = node( distance(start.latlon, end.latlon) * prec, 0, start, None) nodes = {start.id: start_node} todo = [start_node] while todo: #if verbosity >= 4: print("*******************\n"+str(todo)+"\n***************") #if verbosity >= 4: print("*******************\n"+str(sorted(todo))+"\n***************") current = heappop(todo) #if verbosity >= 2: print("visiting",current.f,current.tower.id,current.tower.latlon) #if verbosity >= 2: print("full node",current) if current.tower.id == end.id: return reconstruct_path_direct(current) #if verbosity >=2: # print("CURRENT PATH:\nnode(id:"+(",".join(map(str,reconstruct_path(current)[1])))+");out;") # print("FRONT:\nnode(id:"+(",".join(map(lambda n:str(n.tower.id),todo)))+");out;") neighbours = find_all_neighbours(current.tower, max_fly, eps, safe_dist, minusid, latlon2id, penalty) for neighbour in neighbours: g = current.g + neighbour.dist neighbour_node = nodes.get(neighbour.tower.id, node(None, inf, neighbour.tower, None)) if g < neighbour_node.g: if not neighbour_node.f: nodes[neighbour.tower.id] = neighbour_node must_add = neighbour_node not in todo neighbour_node.origin = current neighbour_node.g = g neighbour_node.f = g + distance(neighbour.tower.latlon, end.latlon) * prec if must_add: #if verbosity >= 3: print("added",neighbour_node) heappush(todo, neighbour_node) #else: #if verbosity >= 3: print("modified",neighbour_node) heapify(todo) return None, [(0., 0., False, start.id, start.latlon[0], start.latlon[1]), (float('inf'), start_node.f, True, end.id, end.latlon[0], end.latlon[1])]
def astar_gt(g, source_id=None, target_id=None): from graph_tool.search import astar_search, AStarVisitor, StopSearch from limic.util import locate_by_id, haversine_distance long = g.vp.long lat = g.vp.lat ids = list(map(lambda x: x[1], g.get_vertices([g.vp.id]))) source, target = locate_by_id(ids, source_id, target_id) source, target = g.vertex(source), g.vertex(target) targetlong, targetlat = long[target], lat[target] class Visitor(AStarVisitor): def __init__(self, target): self.target = target def edge_relaxed(self, e): if e.target() == self.target: raise StopSearch() def distance(x): return haversine_distance(longx=long[x], latx=lat[x], longy=targetlong, laty=targetlat) dist, pred = astar_search(g, source, g.ep.weight, Visitor(target), heuristic=distance) path = [] current = target total_cost = cost = dist[current] if g.vertex(pred[current]) == current: air = True dist = distance(source) else: e = g.edge(g.vertex(pred[current]), g.vertex(current)) dist = g.ep.weight[e] air = True if g.ep.type[e] < 0 else False while True: i = int(current) path.append((cost, dist, air < 0, ids[i], lat[i], long[i])) pre = g.vertex(pred[current]) if pre == current: break cost -= dist current = pre pre = g.vertex(pred[current]) if pre == current: dist = 0. air = False else: e = g.edge(g.vertex(pre), g.vertex(current)) dist = g.ep.weight[e] air = True if g.ep.type[e] < 0 else False path.reverse() if len(path) < 2: assert (len(path) == 1) source = int(source) return None if source != target else 0., [ (0., 0., False, ids[source], lat[source], long[source]) ] + (path if source != target else []) else: return total_cost, path
def osm_post(lim, file_name_out, around=1000, eps=0.01, safe_dist=100, penalize=20): from limic.util import start, end, status, file_size, load_pickled, distance, save_pickled from scipy.spatial import cKDTree as KDTree from networkx import Graph, astar_path_length from pyproj import CRS, Transformer from itertools import chain from limic.overpass import intersect, pylon lines, substations, towers, id2tower, id2node, id2lines, id2types = lim start("Building KD-tree from white nodes") from limic.util import kdtree towers_tree = kdtree(towers, get_latlon=lambda x: x.latlon) end('') status(len(towers)) start("Deleting black nodes") to_delete = set() from limic.util import nodes_in_geometry for substation in substations: to_delete.update( nodes_in_geometry(towers_tree, list(map(lambda x: id2node[x], substation)))) towers = [tower for tower in towers if tower not in to_delete] end('') status(len(towers)) start("Building initial graph") g = Graph() g.add_nodes_from(towers) for line in lines: line_nodes = list(map(lambda x: id2tower[x], line)) for from_node, to_node in zip(line_nodes, line_nodes[1:]): if from_node in to_delete or to_node in to_delete: continue w = distance(from_node.latlon, to_node.latlon) g.add_edge(from_node, to_node, weight=w, type=id2types[from_node.id]) end('') status(len(g.nodes()), end='/') status(len(g.edges())) start("Finding neighbours within " + str(around) + "m") towers_tree = kdtree(towers, get_latlon=lambda x: x.latlon) end('') neighbour_indices, neighbours = towers_tree.get_neighbours(around=1000) end() start("Computing non-logical intersections") tower2index = {} for i, t in zip(range(len(towers)), towers): tower2index[t] = i for k, v in id2lines.items(): id2lines[k] = tuple(map(tuple, v)) end('') segments = set() for u, v in g.edges(): this = (u, v) if u < v else (v, u) ui, vi = tower2index[u], tower2index[v] lines = set() lines.update(id2lines[u.id]) lines.update(id2lines[v.id]) for neighbour in chain(neighbours[ui], neighbours[vi]): if neighbour == u or neighbour == v: continue if not lines.intersection(id2lines[neighbour.id]): for nn in g.neighbors(neighbour): other = (neighbour, nn) if neighbour < nn else (nn, neighbour) segments.add(tuple(sorted((this, other)))) end('') status(len(segments), end=' ') neighbours2intersection = {} minusid = 0 latlon2id = {} segments2intersections = {} for (t1, t2), (t3, t4) in segments: res = intersect(t1.latlon, t2.latlon, t3.latlon, t4.latlon, eps=eps, no_tu=False) if res: intersection, (t, u) = res if not intersection in latlon2id: minusid -= 1 latlon2id[intersection] = minusid segments2intersections.setdefault((t1, t2), []).append( (t, latlon2id[intersection], intersection)) segments2intersections.setdefault((t3, t4), []).append( (u, latlon2id[intersection], intersection)) end('') status(-minusid, end=' ') for (u, v), intersections in segments2intersections.items(): intersections.sort() g.remove_edge(u, v) type = id2types[u.id] assert (type == id2types[v.id]) seq = [u] for _, id, latlon in intersections: seq.append(pylon(id, latlon)) seq.append(v) for from_node, to_node in zip(seq, seq[1:]): w = distance(from_node.latlon, to_node.latlon) g.add_edge(from_node, to_node, weight=w, type=type) end() start("Adding routing through air") airs = set() for ns in neighbours: n = ns[0] for m in ns[1:]: if not g.has_edge(n, m): airs.add((n, m)) end('') for n, m in airs: w = penalize * distance(n.latlon, m.latlon) g.add_edge(n, m, weight=w, type=-1) end('') status(len(g.nodes()), end='/') status(len(g.edges())) from networkx import relabel_nodes start("Prune redundant edges (incomplete)") prune_incomplete(g) end('') status(len(g.edges())) start("Prune redundant edges (complete)") prune_complete(g) end('') status(len(g.edges())) start("Cleaning up graph") relabel = dict( map( lambda tower: (tower, (tower.id, tower.latlon[0], tower.latlon[1])), g.nodes())) relabel_nodes(g, relabel, copy=False) end() start("Saving graph to", file_name_out) save_pickled(file_name_out, g) end('') file_size(file_name_out)