def area_filter(graph, bounds): """Restrict a graph to a geographical area. Removes objects outside bounds from graph. An edge is retained if at least one of its endpoints is inside bounds. A node is retained if it is an endpoint of such an edge (even if the node itself is outside bounds). Arguments: graph -- the Graph object to filter. It is destructively modified. bounds -- a dictionary with keys (minLon, maxLon, minLat, maxLat) describing the bounds of the interesting region. """ def in_bounds(node): """Check if node is within bounds""" return (bounds['minLon'] <= node.lon <= bounds['maxLon'] and bounds['minLat'] <= node.lat <= bounds['maxLat']) def edge_connected_to(edge, nodehash): """Check if edge is connected to a node in the nodehash""" return edge.source.id in nodehash or edge.target.id in nodehash nodes = filter_dict(in_bounds, graph.nodes) edges = filter_dict(lambda edge: edge_connected_to(edge, nodes), graph.edges) node_ids = (set(nodes.keys()) | set([e.source.id for e in edges.values()]) | set([e.target.id for e in edges.values()])) graph.nodes = subdict(graph.nodes, node_ids) graph.edges = edges
def collapse_nodes(graph, node_sets, property_aggregators): """Collapse sets of nodes to single nodes. Replaces each set of nodes in node_sets by a single (new) node and redirects the edges correspondingly. Edges which would end up having both endpoints in the same node are removed. Each new node is positioned at the average of the positions of the node set it represents. It also gets a property containing the original nodes; the name of this property is given by subnode_list_name. Properties from the original nodes may be combined to form aggregate values in the new node. The property_aggregators argument determines how (and whether) this is done. Some useful aggregator functions are sum and avg (for numbers) and lambda lst: ', '.join(map(str, lst)). Arguments: graph -- a Graph object. It is destructively modified. node_sets -- a list of lists of nodes in graph. Each node should occur in exactly one of the lists. subnode_list_name -- name for the property containing the original nodes a newly created node represents. property_aggregators -- describes how to create aggregate properties. Dictionary with names of properties as keys and aggregator functions as corresponding values. Each aggregator function should take a single argument, a list. """ if property_aggregators is None: property_aggregators = {} graph.nodes = {} nodehash = {} for node_set in node_sets: properties = aggregate_properties([x.properties for x in node_set], property_aggregators) new_node = Node( 'cn[%s]' % combine_ids(node_set), avg([n.lon for n in node_set]), avg([n.lat for n in node_set]), properties, ) for node in node_set: nodehash[node.id] = new_node graph.add_node(new_node) # Now nodehash maps original node ids to new node objects. Use it # to redirect the edges to the new nodes: for edge in graph.edges.values(): edge.source = nodehash[edge.source.id] edge.target = nodehash[edge.target.id] graph.edges = filter_dict(lambda e: e.source != e.target, graph.edges)
def collapse_nodes(graph, node_sets, property_aggregators): """Collapse sets of nodes to single nodes. Replaces each set of nodes in node_sets by a single (new) node and redirects the edges correspondingly. Edges which would end up having both endpoints in the same node are removed. Each new node is positioned at the average of the positions of the node set it represents. It also gets a property containing the original nodes; the name of this property is given by subnode_list_name. Properties from the original nodes may be combined to form aggregate values in the new node. The property_aggregators argument determines how (and whether) this is done. Some useful aggregator functions are sum and avg (for numbers) and lambda lst: ', '.join(map(str, lst)). Arguments: graph -- a Graph object. It is destructively modified. node_sets -- a list of lists of nodes in graph. Each node should occur in exactly one of the lists. subnode_list_name -- name for the property containing the original nodes a newly created node represents. property_aggregators -- describes how to create aggregate properties. Dictionary with names of properties as keys and aggregator functions as corresponding values. Each aggregator function should take a single argument, a list. """ if property_aggregators is None: property_aggregators = {} graph.nodes = {} nodehash = {} for node_set in node_sets: properties = aggregate_properties( [x.properties for x in node_set], property_aggregators) new_node = Node('cn[%s]' % combine_ids(node_set), avg([n.lon for n in node_set]), avg([n.lat for n in node_set]), properties) for node in node_set: nodehash[node.id] = new_node graph.add_node(new_node) # Now nodehash maps original node ids to new node objects. Use it # to redirect the edges to the new nodes: for edge in graph.edges.values(): edge.source = nodehash[edge.source.id] edge.target = nodehash[edge.target.id] graph.edges = filter_dict(lambda e: e.source != e.target, graph.edges)