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 os
import pickle import pickle
from collections import OrderedDict from collections import OrderedDict
from itertools import permutations
from django.conf import settings from django.conf import settings
from c3nav.mapdata.models import Level from c3nav.mapdata.models import Level
from c3nav.mapdata.models.geometry import LevelConnector
from c3nav.routing.connection import GraphConnection from c3nav.routing.connection import GraphConnection
from c3nav.routing.level import GraphLevel from c3nav.routing.level import GraphLevel
from c3nav.routing.point import GraphPoint from c3nav.routing.point import GraphPoint
@ -24,6 +24,7 @@ class Graph():
self.points = [] self.points = []
self.connections = [] self.connections = []
self.rooms = [] self.rooms = []
self.no_level_points = []
self.levelconnector_points = {} self.levelconnector_points = {}
self.transfer_points = [] self.transfer_points = []
@ -34,13 +35,13 @@ class Graph():
level.build() level.build()
print('Total:') print('Total:')
self.points = sum((level.points for level in self.levels.values()), [])
print('%d points' % len(self.points)) print('%d points' % len(self.points))
self.rooms = sum((level.rooms for level in self.levels.values()), []) self.rooms = sum((level.rooms for level in self.levels.values()), [])
print('%d rooms' % len(self.rooms)) print('%d rooms' % len(self.rooms))
self.connect_levelconnectors() self.connect_levelconnectors()
print('%d level transfer points' % len(self.no_level_points))
print('%d connections' % len(self.connections)) print('%d connections' % len(self.connections))
print() print()
@ -52,8 +53,14 @@ class Graph():
for i, point in enumerate(self.points): for i, point in enumerate(self.points):
point.i = i 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) 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) 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)
@ -69,15 +76,12 @@ class Graph():
graph = cls() graph = cls()
rooms, points, connections = data 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.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] 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:
point.room.points.append(point)
for room in graph.rooms:
room.level.rooms.append(room)
room.level.points.extend(room.points)
for from_point, to_point, distance in connections: for from_point, to_point, distance in connections:
graph.add_connection(graph.points[from_point], graph.points[to_point], distance) 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) 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(): 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): def add_levelconnector_point(self, levelconnector, point):
self.levelconnector_points.setdefault(levelconnector.name, []).append(point) self.levelconnector_points.setdefault(levelconnector.name, []).append(point)
def connect_levelconnectors(self): def connect_levelconnectors(self):
for levelconnector_name, points in self.levelconnector_points.items(): for levelconnector in LevelConnector.objects.all():
for from_point, to_point in permutations(points, 2): center = levelconnector.geometry.centroid
self.add_connection(from_point, to_point) 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): def add_connection(self, from_point, to_point, distance=None):
self.connections.append(GraphConnection(self, from_point, to_point, distance)) self.connections.append(GraphConnection(self, from_point, to_point, distance))

View file

@ -1,5 +1,4 @@
import os import os
from itertools import permutations
from django.conf import settings from django.conf import settings
from PIL import Image, ImageDraw from PIL import Image, ImageDraw
@ -17,68 +16,72 @@ class GraphLevel():
self.graph = graph self.graph = graph
self.level = level self.level = level
self.points = [] self.points = []
self.no_room_points = []
self.rooms = [] self.rooms = []
def build(self): def build(self):
print('Level %s:' % self.level.name)
self.collect_rooms() 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): def collect_rooms(self):
accessibles = self.level.geometries.accessible accessibles = self.level.geometries.accessible
accessibles = assert_multipolygon(accessibles) accessibles = assert_multipolygon(accessibles)
for geometry in accessibles: for geometry in accessibles:
room = GraphRoom(self, geometry) 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()
def create_doors(self):
doors = self.level.geometries.doors doors = self.level.geometries.doors
doors = assert_multipolygon(doors) doors = assert_multipolygon(doors)
for door in doors: for door in doors:
polygon = door.buffer(0.01, join_style=JOIN_STYLE.mitre) polygon = door.buffer(0.01, join_style=JOIN_STYLE.mitre)
center = door.centroid center = door.centroid
points = [] center_point = GraphPoint(center.x, center.y, level=self)
num_points = 0
for room in self.rooms: for room in self.rooms:
if polygon.intersects(room.geometry): if not polygon.intersects(room.geometry):
nearest_point = get_nearest_point(room.clear_geometry, center) continue
point = GraphPoint(room, *nearest_point.coords[0])
points.append(point)
room.points.append(point)
if len(points) < 2: for subpolygon in assert_multipolygon(polygon.intersection(room.geometry)):
print('door with <2 rooms (%d) detected at (%.2f, %.2f)' % (len(points), center.x, center.y)) 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): if num_points < 2:
from_point.connect_to(to_point) 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(): for levelconnector in self.level.levelconnectors.all():
polygon = levelconnector.geometry polygon = levelconnector.geometry
center = polygon.centroid
for room in self.rooms: for room in self.rooms:
if not polygon.intersects(room.geometry): if not polygon.intersects(room.geometry):
continue continue
point = center for subpolygon in assert_multipolygon(polygon.intersection(room.geometry)):
point = subpolygon.centroid
if not point.within(room.clear_geometry): if not point.within(room.clear_geometry):
point = get_nearest_point(room.clear_geometry, point) point = get_nearest_point(room.clear_geometry, point)
point = GraphPoint(point.x, point.y, room)
point = GraphPoint(room, *point.coords[0])
room.points.append(point)
self.graph.add_levelconnector_point(levelconnector, point) self.graph.add_levelconnector_point(levelconnector, point)
for room in self.rooms: def draw_png(self, points=True, lines=True):
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):
filename = os.path.join(settings.RENDER_ROOT, 'level-%s.base.png' % self.level.name) 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) 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: for point in self.points:
draw.ellipse(_ellipse_bbox(point.x, point.y, height), (200, 0, 0)) draw.ellipse(_ellipse_bbox(point.x, point.y, height), (200, 0, 0))
if transfer_lines: for point in self.no_room_points:
for point in self.points: draw.ellipse(_ellipse_bbox(point.x, point.y, height), (0, 0, 255))
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))
if transfer_points:
for point in self.points: for point in self.points:
if point.in_room_transfer_distances is not None: for otherpoint, connection in point.connections.items():
draw.ellipse(_ellipse_bbox(point.x, point.y, height), (0, 0, 200)) if otherpoint in self.graph.no_level_points:
draw.line(_line_coords(point, otherpoint, height), fill=(0, 255, 255))
im.save(graph_filename) 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, parser.add_argument('--no-lines', action='store_const', dest='lines', const=False, default=True,
help='dont draw lines on the graph image') 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): def handle(self, *args, **options):
graph = Graph.load() graph = Graph.load()
if options['transfer_points'] or options['transfer_lines']: graph.draw_pngs(points=options['points'], lines=options['lines'])
graph.build_router()
graph.draw_pngs(points=options['points'], lines=options['lines'],
transfer_points=options['transfer_points'], transfer_lines=options['transfer_lines'])

View file

@ -4,8 +4,10 @@ from django.utils.functional import cached_property
class GraphPoint(): class GraphPoint():
def __init__(self, room, x, y): def __init__(self, x, y, room=None, level=None, graph=None):
self.room = room 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.x = x
self.y = y self.y = y
self.xy = np.array((x, y)) self.xy = np.array((x, y))
@ -13,6 +15,18 @@ class GraphPoint():
self.connections_in = {} self.connections_in = {}
self.in_room_transfer_distances = None 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 @cached_property
def ellipse_bbox(self): def ellipse_bbox(self):
x = self.x * settings.RENDER_SCALE x = self.x * settings.RENDER_SCALE
@ -20,4 +34,4 @@ class GraphPoint():
return ((x-5, y-5), (x+5, y+5)) return ((x-5, y-5), (x+5, y+5))
def connect_to(self, to_point): 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(): class GraphRoom():
def __init__(self, level, geometry, mpl_paths=None): def __init__(self, level, geometry, mpl_paths=None):
self.level = level self.level = level
self.graph = level.graph
self.geometry = geometry self.geometry = geometry
self.points = [] self.points = []
@ -22,6 +24,10 @@ class GraphRoom():
self.router = Router() self.router = Router()
if not self.empty:
self.level.rooms.append(self)
self.graph.rooms.append(self)
if mpl_paths is not None: if mpl_paths is not None:
self.mpl_paths = mpl_paths self.mpl_paths = mpl_paths
elif not self.empty: elif not self.empty:
@ -105,8 +111,7 @@ class GraphRoom():
return points return points
def add_point(self, coord): def add_point(self, coord):
point = GraphPoint(self, *coord) point = GraphPoint(coord[0], coord[1], self)
self.points.append(point)
return point return point
def connect_points(self): def connect_points(self):