better graph building (routing to be fixed) using points with no room or no level

This commit is contained in:
Laura Klünder 2016-12-06 19:39:42 +01:00
parent e2a13e7f27
commit f0a2f5e058
5 changed files with 96 additions and 79 deletions

View file

@ -1,11 +1,11 @@
import os
import pickle
from collections import OrderedDict
from itertools import permutations
from django.conf import settings
from c3nav.mapdata.models import Level
from c3nav.mapdata.models.geometry import LevelConnector
from c3nav.routing.connection import GraphConnection
from c3nav.routing.level import GraphLevel
from c3nav.routing.point import GraphPoint
@ -24,6 +24,7 @@ class Graph():
self.points = []
self.connections = []
self.rooms = []
self.no_level_points = []
self.levelconnector_points = {}
self.transfer_points = []
@ -34,13 +35,13 @@ class Graph():
level.build()
print('Total:')
self.points = sum((level.points for level in self.levels.values()), [])
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 connections' % len(self.connections))
print()
@ -52,8 +53,14 @@ 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_paths) for room in self.rooms)
points = tuple((point.room.i, point.x, point.y) for point in self.points)
points = tuple((point.x, point.y, i_or_none(point.room), name_or_none(point.level)) 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)
@ -69,15 +76,12 @@ 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]], room[1], room[2]) for room in rooms]
graph.points = [GraphPoint(graph.rooms[point[0]], point[1], point[2]) for point in points]
for point in graph.points:
point.room.points.append(point)
for room in graph.rooms:
room.level.rooms.append(room)
room.level.points.extend(room.points)
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 from_point, to_point, distance in connections:
graph.add_connection(graph.points[from_point], graph.points[to_point], distance)
@ -99,17 +103,20 @@ class Graph():
self.router.build(self.transfer_points, global_routing=True)
def draw_pngs(self, points=True, lines=True, transfer_points=False, transfer_lines=False):
def draw_pngs(self, points=True, lines=True):
for level in self.levels.values():
level.draw_png(points, lines, transfer_points, transfer_lines)
level.draw_png(points, lines)
def add_levelconnector_point(self, levelconnector, point):
self.levelconnector_points.setdefault(levelconnector.name, []).append(point)
def connect_levelconnectors(self):
for levelconnector_name, points in self.levelconnector_points.items():
for from_point, to_point in permutations(points, 2):
self.add_connection(from_point, to_point)
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, []):
center_point.connect_to(point)
point.connect_to(center_point)
def add_connection(self, from_point, to_point, distance=None):
self.connections.append(GraphConnection(self, from_point, to_point, distance))

View file

@ -1,5 +1,4 @@
import os
from itertools import permutations
from django.conf import settings
from PIL import Image, ImageDraw
@ -17,68 +16,72 @@ class GraphLevel():
self.graph = graph
self.level = level
self.points = []
self.no_room_points = []
self.rooms = []
def build(self):
print('Level %s:' % self.level.name)
self.collect_rooms()
self.create_points()
print('%d rooms' % len(self.rooms))
for room in self.rooms:
room.create_points()
self.create_doors()
self.create_levelconnectors()
for room in self.rooms:
room.connect_points()
print('%d points' % len(self.points))
print('%d room transfer points' % len(self.no_room_points))
print()
def collect_rooms(self):
accessibles = self.level.geometries.accessible
accessibles = assert_multipolygon(accessibles)
for geometry in accessibles:
room = GraphRoom(self, geometry)
if not room.empty:
self.rooms.append(room)
def create_points(self):
print('Level %s:' % self.level.name)
for room in self.rooms:
room.create_points()
GraphRoom(self, geometry)
def create_doors(self):
doors = self.level.geometries.doors
doors = assert_multipolygon(doors)
for door in doors:
polygon = door.buffer(0.01, join_style=JOIN_STYLE.mitre)
center = door.centroid
points = []
center_point = GraphPoint(center.x, center.y, level=self)
num_points = 0
for room in self.rooms:
if polygon.intersects(room.geometry):
nearest_point = get_nearest_point(room.clear_geometry, center)
point = GraphPoint(room, *nearest_point.coords[0])
points.append(point)
room.points.append(point)
if not polygon.intersects(room.geometry):
continue
if len(points) < 2:
print('door with <2 rooms (%d) detected at (%.2f, %.2f)' % (len(points), center.x, center.y))
for subpolygon in assert_multipolygon(polygon.intersection(room.geometry)):
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
for from_point, to_point in permutations(points, 2):
from_point.connect_to(to_point)
if num_points < 2:
print('door with <2 num_points (%d) detected at (%.2f, %.2f)' % (num_points, center.x, center.y))
def create_levelconnectors(self):
for levelconnector in self.level.levelconnectors.all():
polygon = levelconnector.geometry
center = polygon.centroid
for room in self.rooms:
if not polygon.intersects(room.geometry):
continue
point = center
if not point.within(room.clear_geometry):
point = get_nearest_point(room.clear_geometry, point)
for subpolygon in assert_multipolygon(polygon.intersection(room.geometry)):
point = subpolygon.centroid
if not point.within(room.clear_geometry):
point = get_nearest_point(room.clear_geometry, point)
point = GraphPoint(point.x, point.y, room)
self.graph.add_levelconnector_point(levelconnector, point)
point = GraphPoint(room, *point.coords[0])
room.points.append(point)
self.graph.add_levelconnector_point(levelconnector, point)
for room in self.rooms:
room.connect_points()
self.points = sum((room.points for room in self.rooms), [])
print('%d points' % len(self.points))
print()
def draw_png(self, points=True, lines=True, transfer_points=False, transfer_lines=False):
def draw_png(self, points=True, lines=True):
filename = os.path.join(settings.RENDER_ROOT, 'level-%s.base.png' % self.level.name)
graph_filename = os.path.join(settings.RENDER_ROOT, 'level-%s.graph.png' % self.level.name)
@ -94,15 +97,12 @@ class GraphLevel():
for point in self.points:
draw.ellipse(_ellipse_bbox(point.x, point.y, height), (200, 0, 0))
if transfer_lines:
for point in self.points:
if point.in_room_transfer_distances is not None:
for otherpoint, distance in point.in_room_transfer_distances.items():
draw.line(_line_coords(point, otherpoint, height), fill=(100, 100, 255))
for point in self.no_room_points:
draw.ellipse(_ellipse_bbox(point.x, point.y, height), (0, 0, 255))
if transfer_points:
for point in self.points:
if point.in_room_transfer_distances is not None:
draw.ellipse(_ellipse_bbox(point.x, point.y, height), (0, 0, 200))
for point in self.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))
im.save(graph_filename)

View file

@ -13,15 +13,6 @@ class Command(BaseCommand):
parser.add_argument('--no-lines', action='store_const', dest='lines', const=False, default=True,
help='dont draw lines on the graph image')
parser.add_argument('--transfer-points', action='store_const', const=True, default=False,
help='highlight transfer points')
parser.add_argument('--transfer-lines', action='store_const', const=True, default=False,
help='draw in-room transfer lines')
def handle(self, *args, **options):
graph = Graph.load()
if options['transfer_points'] or options['transfer_lines']:
graph.build_router()
graph.draw_pngs(points=options['points'], lines=options['lines'],
transfer_points=options['transfer_points'], transfer_lines=options['transfer_lines'])
graph.draw_pngs(points=options['points'], lines=options['lines'])

View file

@ -4,8 +4,10 @@ from django.utils.functional import cached_property
class GraphPoint():
def __init__(self, room, x, y):
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
self.x = x
self.y = y
self.xy = np.array((x, y))
@ -13,6 +15,18 @@ class GraphPoint():
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):
x = self.x * settings.RENDER_SCALE
@ -20,4 +34,4 @@ class GraphPoint():
return ((x-5, y-5), (x+5, y+5))
def connect_to(self, to_point):
self.room.level.graph.add_connection(self, to_point)
self.graph.add_connection(self, to_point)

View file

@ -14,6 +14,8 @@ from c3nav.routing.utils.mpl import polygon_to_mpl_paths
class GraphRoom():
def __init__(self, level, geometry, mpl_paths=None):
self.level = level
self.graph = level.graph
self.geometry = geometry
self.points = []
@ -22,10 +24,14 @@ class GraphRoom():
self.router = Router()
if mpl_paths is not None:
self.mpl_paths = mpl_paths
elif not self.empty:
self.mpl_paths = polygon_to_mpl_paths(self.clear_geometry.buffer(0.01, join_style=JOIN_STYLE.mitre))
if not self.empty:
self.level.rooms.append(self)
self.graph.rooms.append(self)
if mpl_paths is not None:
self.mpl_paths = mpl_paths
elif not self.empty:
self.mpl_paths = polygon_to_mpl_paths(self.clear_geometry.buffer(0.01, join_style=JOIN_STYLE.mitre))
def create_points(self):
original_geometry = self.geometry
@ -105,8 +111,7 @@ class GraphRoom():
return points
def add_point(self, coord):
point = GraphPoint(self, *coord)
self.points.append(point)
point = GraphPoint(coord[0], coord[1], self)
return point
def connect_points(self):