better graph building (routing to be fixed) using points with no room or no level
This commit is contained in:
parent
e2a13e7f27
commit
f0a2f5e058
5 changed files with 96 additions and 79 deletions
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue