2016-12-05 12:09:43 +01:00
|
|
|
import os
|
|
|
|
import pickle
|
|
|
|
from collections import OrderedDict
|
2016-12-04 12:50:32 +01:00
|
|
|
|
2016-12-13 23:00:38 +01:00
|
|
|
import numpy as np
|
2016-12-05 12:09:43 +01:00
|
|
|
from django.conf import settings
|
|
|
|
|
2016-12-03 19:09:39 +01:00
|
|
|
from c3nav.mapdata.models import Level
|
2016-12-06 19:39:42 +01:00
|
|
|
from c3nav.mapdata.models.geometry import LevelConnector
|
2016-12-05 13:56:54 +01:00
|
|
|
from c3nav.routing.level import GraphLevel
|
|
|
|
from c3nav.routing.point import GraphPoint
|
2016-12-03 19:09:39 +01:00
|
|
|
|
|
|
|
|
2016-12-09 20:01:08 +01:00
|
|
|
class Graph:
|
2016-12-16 11:38:25 +01:00
|
|
|
graph_cached = None
|
|
|
|
graph_cached_date = None
|
2016-12-05 12:09:43 +01:00
|
|
|
default_filename = os.path.join(settings.DATA_DIR, 'graph.pickle')
|
|
|
|
|
2016-12-03 19:09:39 +01:00
|
|
|
def __init__(self):
|
2016-12-05 12:09:43 +01:00
|
|
|
self.levels = OrderedDict()
|
2016-12-04 14:03:11 +01:00
|
|
|
for level in Level.objects.all():
|
|
|
|
self.levels[level.name] = GraphLevel(self, level)
|
|
|
|
|
2016-12-10 12:19:31 +01:00
|
|
|
self.points = []
|
2016-12-13 23:00:38 +01:00
|
|
|
self.level_transfer_points = None
|
2016-12-05 13:39:22 +01:00
|
|
|
|
2016-12-10 12:07:52 +01:00
|
|
|
# Building the Graph
|
2016-12-03 19:09:39 +01:00
|
|
|
def build(self):
|
2016-12-13 23:00:38 +01:00
|
|
|
self._built_level_transfer_points = []
|
|
|
|
self._built_levelconnector_points = {}
|
|
|
|
|
2016-12-03 19:09:39 +01:00
|
|
|
for level in self.levels.values():
|
|
|
|
level.build()
|
2016-12-04 12:50:32 +01:00
|
|
|
|
2016-12-10 12:07:52 +01:00
|
|
|
# collect rooms and points
|
2016-12-13 23:00:38 +01:00
|
|
|
rooms = sum((level.rooms for level in self.levels.values()), [])
|
|
|
|
self.points = sum((level._built_points for level in self.levels.values()), [])
|
2016-12-05 12:09:43 +01:00
|
|
|
|
2016-12-10 12:07:52 +01:00
|
|
|
# create connections between levels
|
2016-12-09 20:01:08 +01:00
|
|
|
print()
|
2016-12-04 12:50:32 +01:00
|
|
|
self.connect_levelconnectors()
|
|
|
|
|
2016-12-13 23:00:38 +01:00
|
|
|
# finishing build: creating numpy arrays and convert everything else to tuples
|
2016-12-14 00:39:32 +01:00
|
|
|
self.points = tuple(set(self.points))
|
2016-12-10 12:07:52 +01:00
|
|
|
|
2016-12-13 23:00:38 +01:00
|
|
|
for i, room in enumerate(rooms):
|
2016-12-10 12:07:52 +01:00
|
|
|
room.i = i
|
|
|
|
|
|
|
|
for i, point in enumerate(self.points):
|
|
|
|
point.i = i
|
|
|
|
|
2016-12-13 23:00:38 +01:00
|
|
|
self.level_transfer_points = np.array(tuple(point.i for point in self._built_level_transfer_points))
|
|
|
|
|
|
|
|
for level in self.levels.values():
|
|
|
|
level.finish_build()
|
|
|
|
|
2016-12-09 20:01:08 +01:00
|
|
|
print()
|
|
|
|
print('Total:')
|
2016-12-13 23:00:38 +01:00
|
|
|
self.print_stats()
|
2016-12-09 20:01:08 +01:00
|
|
|
|
2016-12-05 12:09:43 +01:00
|
|
|
print()
|
2016-12-09 20:01:08 +01:00
|
|
|
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)))
|
2016-12-05 12:09:43 +01:00
|
|
|
|
2016-12-13 23:00:38 +01:00
|
|
|
def print_stats(self):
|
|
|
|
print('%d points' % len(self.points))
|
|
|
|
print('%d rooms' % sum(len(level.rooms) for level in self.levels.values()))
|
|
|
|
print('%d level transfer points' % len(self.level_transfer_points))
|
2016-12-14 00:39:46 +01:00
|
|
|
print('%d connections' % sum(level.connection_count() for level in self.levels.values()))
|
2016-12-05 12:09:43 +01:00
|
|
|
|
2016-12-10 12:07:52 +01:00
|
|
|
def add_levelconnector_point(self, levelconnector, point):
|
2016-12-13 23:00:38 +01:00
|
|
|
self._built_levelconnector_points.setdefault(levelconnector.name, []).append(point)
|
2016-12-05 12:09:43 +01:00
|
|
|
|
2016-12-10 12:07:52 +01:00
|
|
|
def connect_levelconnectors(self):
|
|
|
|
for levelconnector in LevelConnector.objects.all():
|
|
|
|
center = levelconnector.geometry.centroid
|
2016-12-13 23:00:38 +01:00
|
|
|
points = self._built_levelconnector_points.get(levelconnector.name, [])
|
2016-12-13 23:32:19 +01:00
|
|
|
rooms = set(point.room for point in points if point.room is not None)
|
|
|
|
connected_levels = set(room.level for room in rooms)
|
2016-12-10 12:07:52 +01:00
|
|
|
|
2016-12-13 23:32:19 +01:00
|
|
|
should_levels = tuple(level.name for level in levelconnector.levels.all())
|
|
|
|
missing_levels = set(should_levels) - set(level.level.name for level in connected_levels)
|
|
|
|
|
|
|
|
if missing_levels:
|
|
|
|
print('levelconnector %s on levels %s at (%.2f, %.2f) is not connected to levels %s!' %
|
|
|
|
(levelconnector.name, ', '.join(should_levels), center.x, center.y, ', '.join(missing_levels)))
|
2016-12-10 12:07:52 +01:00
|
|
|
continue
|
|
|
|
|
2016-12-13 23:32:19 +01:00
|
|
|
center_point = GraphPoint(center.x, center.y, None)
|
2016-12-10 12:07:52 +01:00
|
|
|
self.points.append(center_point)
|
2016-12-13 23:00:38 +01:00
|
|
|
self._built_level_transfer_points.append(center_point)
|
2016-12-10 12:07:52 +01:00
|
|
|
|
2016-12-13 23:32:19 +01:00
|
|
|
for level in connected_levels:
|
2016-12-13 23:00:38 +01:00
|
|
|
level._built_room_transfer_points.append(center_point)
|
|
|
|
level._built_points.append(center_point)
|
2016-12-10 12:07:52 +01:00
|
|
|
|
|
|
|
for room in rooms:
|
2016-12-13 23:00:38 +01:00
|
|
|
room._built_points.append(center_point)
|
2016-12-10 12:07:52 +01:00
|
|
|
|
|
|
|
for point in points:
|
2016-12-13 23:00:38 +01:00
|
|
|
center_point.connect_to(point)
|
|
|
|
point.connect_to(center_point)
|
2016-12-10 12:07:52 +01:00
|
|
|
|
|
|
|
# Loading/Saving the Graph
|
|
|
|
def serialize(self):
|
2016-12-13 23:00:38 +01:00
|
|
|
return (
|
|
|
|
{name: level.serialize() for name, level in self.levels.items()},
|
|
|
|
[point.serialize() for point in self.points],
|
|
|
|
self.level_transfer_points,
|
|
|
|
)
|
2016-12-05 12:09:43 +01:00
|
|
|
|
|
|
|
def save(self, filename=None):
|
|
|
|
if filename is None:
|
|
|
|
filename = self.default_filename
|
|
|
|
with open(filename, 'wb') as f:
|
|
|
|
pickle.dump(self.serialize(), f)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def unserialize(cls, data):
|
2016-12-13 23:00:38 +01:00
|
|
|
levels, points, level_transfer_points = data
|
2016-12-05 12:09:43 +01:00
|
|
|
|
2016-12-13 23:00:38 +01:00
|
|
|
graph = cls()
|
2016-12-05 12:09:43 +01:00
|
|
|
|
2016-12-13 23:32:19 +01:00
|
|
|
for name, level in levels.items():
|
|
|
|
graph.levels[name].unserialize(level)
|
|
|
|
|
|
|
|
rooms = sum((level.rooms for level in graph.levels.values()), ())
|
|
|
|
|
|
|
|
graph.points = tuple(GraphPoint(x, y, None if room is None else rooms[room]) for x, y, room in points)
|
2016-12-13 23:00:38 +01:00
|
|
|
graph.level_transfer_points = level_transfer_points
|
2016-12-05 12:09:43 +01:00
|
|
|
|
|
|
|
return graph
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def load(cls, filename=None):
|
2016-12-16 11:38:25 +01:00
|
|
|
do_cache = False
|
2016-12-05 12:09:43 +01:00
|
|
|
if filename is None:
|
2016-12-16 11:38:25 +01:00
|
|
|
do_cache = True
|
2016-12-05 12:09:43 +01:00
|
|
|
filename = cls.default_filename
|
2016-12-16 11:38:25 +01:00
|
|
|
|
|
|
|
graph_mtime = None
|
|
|
|
if do_cache:
|
|
|
|
graph_mtime = os.path.getmtime(filename)
|
|
|
|
if cls.graph_cached is not None:
|
|
|
|
if cls.graph_cached_date == graph_mtime:
|
|
|
|
return cls.graph_cached
|
|
|
|
|
2016-12-05 12:09:43 +01:00
|
|
|
with open(filename, 'rb') as f:
|
|
|
|
graph = cls.unserialize(pickle.load(f))
|
2016-12-16 11:38:25 +01:00
|
|
|
|
|
|
|
if do_cache:
|
|
|
|
cls.graph_cached_date = graph_mtime
|
|
|
|
cls.graph_cached = graph
|
|
|
|
print(cls.graph_cached, cls.graph_cached_date)
|
|
|
|
|
2016-12-13 23:00:38 +01:00
|
|
|
graph.print_stats()
|
2016-12-05 12:09:43 +01:00
|
|
|
return graph
|
|
|
|
|
2016-12-10 12:07:52 +01:00
|
|
|
# Drawing
|
2016-12-06 19:39:42 +01:00
|
|
|
def draw_pngs(self, points=True, lines=True):
|
2016-12-04 12:50:32 +01:00
|
|
|
for level in self.levels.values():
|
2016-12-06 19:39:42 +01:00
|
|
|
level.draw_png(points, lines)
|
2016-12-03 19:09:39 +01:00
|
|
|
|
2016-12-10 12:07:52 +01:00
|
|
|
# Router
|
|
|
|
def build_router(self):
|
|
|
|
for level in self.levels.values():
|
|
|
|
level.build_router()
|