Beispiel #1
0
def create_places(graph, bounds, viewport_size, limit):
    """Convert a graph of rooms to a graph of 'places'.

    A 'place' is a set of one or more rooms.  The position of a place
    is the average of the positions of its rooms.  The places are
    created such that no two places are closer than limit to each
    other.  Each place node has a property 'rooms' (available as
    placenode.properties['rooms']) which is a list of the room nodes
    it is based on.

    Arguments:

    graph -- a Graph object.  It is destructively modified.

    bounds -- a dictionary with keys (minLon, maxLon, minLat, maxLat)
    describing the bounds of the interesting region.

    viewport_size -- a dictionary with keys (width, height), the width
    and height of the user's viewport for the map in pixels.

    limit -- the minimum distance (in pixels) there may be between two
    points without them being collapsed to one.

    """
    # TODO:
    #
    # -- This may give division by zero with bogus input (should check
    #    for zeros -- what should we do then?)
    #
    # -- Should take into account that longitudes wrap around. Is
    #    there any way to detect whether we have a map wider than the
    #    earth, or do we need an extra parameter?
    width = bounds['maxLon'] - bounds['minLon']
    height = bounds['maxLat'] - bounds['minLat']
    lon_scale = float(viewport_size['width']) / width
    lat_scale = float(viewport_size['height']) / height

    def square(var):
        """Square a number"""
        return var * var

    def distance(node1, node2):
        """Calculate distance from node1 to node2"""
        return sqrt(square((node1.lon - node2.lon) * lon_scale) +
                    square((node1.lat - node2.lat) * lat_scale))

    places = []
    for node in graph.nodes.values():
        for place in places:
            if distance(node, place['position']) < limit:
                place['rooms'].append(node)
                place['position'].lon = avg([n.lon for n in place['rooms']])
                place['position'].lat = avg([n.lat for n in place['rooms']])
                break
        else:
            places.append({'position': Node(None, node.lon, node.lat, None),
                           'rooms': [node]})
    collapse_nodes(graph,
                   [place['rooms'] for place in places],
                   AGGREGATE_PROPERTIES_PLACE)
Beispiel #2
0
Datei: graph.py Projekt: hmpf/nav
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)
Beispiel #3
0
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)