From 23139ecdf8d7152e216e0579fb5452f17f1761ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Fri, 9 Dec 2016 20:01:08 +0100 Subject: [PATCH] buildgraph: points with multiple rooms instead of points with no rooms --- src/c3nav/routing/graph.py | 78 ++++++++++++------- src/c3nav/routing/level.py | 48 ++++++++---- .../routing/management/commands/buildgraph.py | 1 + src/c3nav/routing/point.py | 23 ++---- src/c3nav/routing/room.py | 26 +++---- 5 files changed, 103 insertions(+), 73 deletions(-) diff --git a/src/c3nav/routing/graph.py b/src/c3nav/routing/graph.py index 9eddc79e..9112a340 100644 --- a/src/c3nav/routing/graph.py +++ b/src/c3nav/routing/graph.py @@ -13,7 +13,7 @@ from c3nav.routing.room import GraphRoom from c3nav.routing.router import Router -class Graph(): +class Graph: default_filename = os.path.join(settings.DATA_DIR, 'graph.pickle') def __init__(self): @@ -24,27 +24,31 @@ class Graph(): self.points = [] self.connections = [] self.rooms = [] - self.no_level_points = [] - self.levelconnector_points = {} - self.transfer_points = [] - self.router = Router() + self.level_transfer_points = [] + self.levelconnector_points = {} def build(self): for level in self.levels.values(): level.build() + self.rooms = sum((level.rooms for level in self.levels.values()), []) + self.points = sum((level.points for level in self.levels.values()), []) + + print() + self.connect_levelconnectors() + + print() print('Total:') print('%d points' % len(self.points)) - - self.rooms = sum((level.rooms for level in self.levels.values()), []) print('%d rooms' % len(self.rooms)) - - self.connect_levelconnectors() - print('%d level transfer points' % len(self.no_level_points)) - + print('%d level transfer points' % len(self.level_transfer_points)) print('%d connections' % len(self.connections)) + print() + print('Points per room:') + for name, level in self.levels.items(): + print(('Level %s:' % name), *(sorted((len(room.points) for room in level.rooms), reverse=True))) def serialize(self): for i, room in enumerate(self.rooms): @@ -53,17 +57,11 @@ class Graph(): for i, point in enumerate(self.points): point.i = i - def i_or_none(obj): - return None if obj is None else obj.i - - def name_or_none(obj): - return None if obj is None else obj.level.name - - rooms = tuple((room.level.level.name, room.geometry, room.mpl_clear) for room in self.rooms) - points = tuple((point.x, point.y, i_or_none(point.room), name_or_none(point.level)) for point in self.points) + rooms = tuple((room.level.level.name, room.mpl_clear) for room in self.rooms) + points = tuple((point.x, point.y, tuple(room.i for room in point.rooms)) for point in self.points) connections = tuple((conn.from_point.i, conn.to_point.i, conn.distance) for conn in self.connections) - return (rooms, points, connections) + return rooms, points, connections def save(self, filename=None): if filename is None: @@ -76,12 +74,18 @@ class Graph(): graph = cls() rooms, points, connections = data - def by_key_or_none(collection, key): - return None if key is None else collection[key] + graph.rooms = [GraphRoom(graph.levels[room[0]], mpl_clear=room[1]) for room in rooms] + graph.points = [GraphPoint(point[0], point[1], rooms=tuple(graph.rooms[i] for i in point[2])) + for point in points] - graph.rooms = [GraphRoom(graph.levels[room[0]], room[1], room[2]) for room in rooms] - graph.points = [GraphPoint(point[0], point[1], by_key_or_none(graph.rooms, point[2]), - by_key_or_none(graph.levels, point[3]), graph) for point in points] + for point in graph.points: + for room in point.rooms: + room.points.append(point) + + for name, level in graph.levels.items(): + level.rooms = [room for room in graph.rooms if room.level == level] + level.points = list(set(sum((room.points for room in level.rooms), []))) + level.room_transfer_points = [point for point in level.points if len(point.rooms) > 1] for from_point, to_point, distance in connections: graph.add_connection(graph.points[from_point], graph.points[to_point], distance) @@ -101,6 +105,7 @@ class Graph(): room.build_router() self.transfer_points.extend(room.router.transfer_points) + self.router = Router() self.router.build(self.transfer_points, global_routing=True) def draw_pngs(self, points=True, lines=True): @@ -113,8 +118,27 @@ class Graph(): def connect_levelconnectors(self): for levelconnector in LevelConnector.objects.all(): center = levelconnector.geometry.centroid - center_point = GraphPoint(center.x, center.y, graph=self) - for point in self.levelconnector_points.get(levelconnector.name, []): + points = self.levelconnector_points.get(levelconnector.name, []) + rooms = tuple(set(sum((point.rooms for point in points), []))) + + if len(rooms) < 2: + print('levelconnector %s on levels %s at (%.2f, %.2f) has <2 rooms (%d%s)!' % + (levelconnector.name, ', '.join(level.name for level in levelconnector.levels.all()), + center.x, center.y, len(rooms), (' on level '+rooms[0].level.level.name) if rooms else '')) + continue + + center_point = GraphPoint(center.x, center.y, rooms=rooms) + self.points.append(center_point) + + levels = tuple(set(room.level for room in rooms)) + for level in levels: + level.room_transfer_points.append(center_point) + level.points.append(center_point) + + for room in rooms: + room.points.append(center_point) + + for point in points: center_point.connect_to(point) point.connect_to(center_point) diff --git a/src/c3nav/routing/level.py b/src/c3nav/routing/level.py index 730c4764..7b5791c7 100644 --- a/src/c3nav/routing/level.py +++ b/src/c3nav/routing/level.py @@ -15,34 +15,39 @@ class GraphLevel(): def __init__(self, graph, level): self.graph = graph self.level = level + self.points = [] - self.no_room_points = [] + self.room_transfer_points = [] self.rooms = [] def build(self): + print() print('Level %s:' % self.level.name) self.collect_rooms() print('%d rooms' % len(self.rooms)) for room in self.rooms: - room.prepare_build() room.build_points() self.create_doors() self.create_levelconnectors() + self.points = sum((room.points for room in self.rooms), []) + self.points.extend(self.room_transfer_points) + for room in self.rooms: - room.build_connections() + pass # room.build_connections() print('%d points' % len(self.points)) - print('%d room transfer points' % len(self.no_room_points)) - print() + print('%d room transfer points' % len(self.room_transfer_points)) def collect_rooms(self): accessibles = self.level.geometries.accessible accessibles = assert_multipolygon(accessibles) for geometry in accessibles: - GraphRoom(self, geometry) + room = GraphRoom(self, geometry) + if room.prepare_build(): + self.rooms.append(room) def create_doors(self): doors = self.level.geometries.doors @@ -50,22 +55,33 @@ class GraphLevel(): for door in doors: polygon = door.buffer(0.01, join_style=JOIN_STYLE.mitre) center = door.centroid - center_point = GraphPoint(center.x, center.y, level=self) num_points = 0 + connected_rooms = set() + points = [] for room in self.rooms: if not polygon.intersects(room.geometry): continue for subpolygon in assert_multipolygon(polygon.intersection(room.geometry)): + connected_rooms.add(room) nearest_point = get_nearest_point(room.clear_geometry, subpolygon.centroid) point = GraphPoint(nearest_point.x, nearest_point.y, room) - center_point.connect_to(point) - point.connect_to(center_point) - num_points += 1 + room.points.append(point) + points.append(point) - if num_points < 2: - print('door with <2 num_points (%d) detected at (%.2f, %.2f)' % (num_points, center.x, center.y)) + if len(points) < 2: + print('door with <2 points (%d) detected at (%.2f, %.2f)' % (num_points, center.x, center.y)) + continue + + center_point = GraphPoint(center.x, center.y, rooms=tuple(connected_rooms)) + self.room_transfer_points.append(center_point) + for room in connected_rooms: + room.points.append(center_point) + + for point in points: + center_point.connect_to(point) + point.connect_to(center_point) def create_levelconnectors(self): for levelconnector in self.level.levelconnectors.all(): @@ -80,6 +96,7 @@ class GraphLevel(): if not point.within(room.clear_geometry): point = get_nearest_point(room.clear_geometry, point) point = GraphPoint(point.x, point.y, room) + room.points.append(point) self.graph.add_levelconnector_point(levelconnector, point) def draw_png(self, points=True, lines=True): @@ -98,12 +115,11 @@ class GraphLevel(): for point in self.points: draw.ellipse(_ellipse_bbox(point.x, point.y, height), (200, 0, 0)) - for point in self.no_room_points: + for point in self.room_transfer_points: draw.ellipse(_ellipse_bbox(point.x, point.y, height), (0, 0, 255)) - for point in self.points: + for point in self.room_transfer_points: for otherpoint, connection in point.connections.items(): - if otherpoint in self.graph.no_level_points: - draw.line(_line_coords(point, otherpoint, height), fill=(0, 255, 255)) + draw.line(_line_coords(point, otherpoint, height), fill=(0, 255, 255)) im.save(graph_filename) diff --git a/src/c3nav/routing/management/commands/buildgraph.py b/src/c3nav/routing/management/commands/buildgraph.py index 0379f831..63ac017d 100644 --- a/src/c3nav/routing/management/commands/buildgraph.py +++ b/src/c3nav/routing/management/commands/buildgraph.py @@ -12,6 +12,7 @@ class Command(BaseCommand): start = time.time() graph = Graph() graph.build() + print() print('Built in %.4fs' % (time.time() - start)) start = time.time() diff --git a/src/c3nav/routing/point.py b/src/c3nav/routing/point.py index 60c7d833..d83916dc 100644 --- a/src/c3nav/routing/point.py +++ b/src/c3nav/routing/point.py @@ -4,28 +4,17 @@ from django.utils.functional import cached_property class GraphPoint(): - def __init__(self, x, y, room=None, level=None, graph=None): - self.room = room - self.level = room.level if level is None and room is not None else level - self.graph = self.level.graph if graph is None and self.level is not None else graph + def __init__(self, x, y, room=None, rooms=None): + self.rooms = rooms if rooms is not None else [room] self.x = x self.y = y self.xy = np.array((x, y)) + + # self.level = room.level + self.graph = self.rooms[0].graph + self.connections = {} self.connections_in = {} - self.in_room_transfer_distances = None - - if self.room is not None: - self.room.points.append(self) - elif self.level is not None: - self.level.no_room_points.append(self) - - if self.level is not None: - self.level.points.append(self) - else: - self.graph.no_level_points.append(self) - - self.graph.points.append(self) @cached_property def ellipse_bbox(self): diff --git a/src/c3nav/routing/room.py b/src/c3nav/routing/room.py index 396fc600..a2c1c7da 100644 --- a/src/c3nav/routing/room.py +++ b/src/c3nav/routing/room.py @@ -12,30 +12,27 @@ from c3nav.routing.utils.mpl import shapely_to_mpl class GraphRoom(): - def __init__(self, level, geometry, mpl_clear=None): + def __init__(self, level, geometry=None, mpl_clear=None): self.level = level self.graph = level.graph self.geometry = geometry - self.points = [] - - self.clear_geometry = geometry.buffer(-0.3, join_style=JOIN_STYLE.mitre) - self.empty = self.clear_geometry.is_empty - - self.router = Router() - - if not self.empty: - self.level.rooms.append(self) - self.graph.rooms.append(self) - self.mpl_clear = mpl_clear + self.points = [] + def prepare_build(self): + self.clear_geometry = self.geometry.buffer(-0.3, join_style=JOIN_STYLE.mitre) + + if self.clear_geometry.is_empty: + return False + self.mpl_clear = shapely_to_mpl(self.clear_geometry.buffer(0.01, join_style=JOIN_STYLE.mitre)) self.mpl_stairs = () for stair_line in assert_multilinestring(self.level.level.geometries.stairs): coords = tuple(stair_line.coords) self.mpl_stairs += tuple((Path(part), coord_angle(*part)) for part in zip(coords[:-1], coords[1:])) + return True def build_points(self): original_geometry = self.geometry @@ -127,11 +124,13 @@ class GraphRoom(): if not self.mpl_clear.contains_point(coord): return [] point = GraphPoint(coord[0], coord[1], self) + self.points.append(point) return [point] def build_connections(self): i = 0 - for point1, point2 in combinations(self.points, 2): + own_points = [point for point in self.points if point not in self.level.room_transfer_points] + for point1, point2 in combinations(own_points, 2): path = Path(np.vstack((point1.xy, point2.xy))) # lies within room @@ -159,4 +158,5 @@ class GraphRoom(): i += 1 def build_router(self): + self.router = Router() self.router.build(self.points)