first stuff for showing the route on the site
This commit is contained in:
parent
ad58ddbbcb
commit
f6129d621c
14 changed files with 366 additions and 154 deletions
|
@ -1,11 +1,40 @@
|
||||||
|
import os
|
||||||
|
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from c3nav.mapdata.render.utils import get_render_path
|
from c3nav.mapdata.utils.misc import get_render_path
|
||||||
|
|
||||||
|
|
||||||
def get_level_image(request, level):
|
class LevelComposer:
|
||||||
im = Image.open(get_render_path('png', level.name, 'full', True))
|
images = {}
|
||||||
response = HttpResponse(content_type="image/png")
|
images_mtimes = {}
|
||||||
im.save(response, 'PNG')
|
|
||||||
return response
|
@classmethod
|
||||||
|
def _get_image(cls, filename, cached=True):
|
||||||
|
mtime = None
|
||||||
|
if cached:
|
||||||
|
mtime = os.path.getmtime(filename)
|
||||||
|
if filename in cls.images:
|
||||||
|
if cls.images_mtimes[filename] == mtime:
|
||||||
|
return cls.images[filename]
|
||||||
|
|
||||||
|
img = Image.open(filename)
|
||||||
|
|
||||||
|
if cached:
|
||||||
|
cls.images[filename] = img
|
||||||
|
cls.images_mtimes[filename] = mtime
|
||||||
|
|
||||||
|
return img
|
||||||
|
|
||||||
|
def _get_public_level_image(self, level):
|
||||||
|
return self._get_image(get_render_path('png', level.name, 'full', True))
|
||||||
|
|
||||||
|
def get_level_image(self, request, level):
|
||||||
|
img = self._get_public_level_image(level)
|
||||||
|
response = HttpResponse(content_type="image/png")
|
||||||
|
img.save(response, 'PNG')
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
composer = LevelComposer()
|
||||||
|
|
|
@ -5,7 +5,7 @@ from django.conf import settings
|
||||||
from shapely.affinity import scale
|
from shapely.affinity import scale
|
||||||
from shapely.geometry import JOIN_STYLE, box
|
from shapely.geometry import JOIN_STYLE, box
|
||||||
|
|
||||||
from c3nav.mapdata.render.utils import get_dimensions, get_render_path
|
from c3nav.mapdata.utils.misc import get_dimensions, get_render_dimensions, get_render_path
|
||||||
|
|
||||||
|
|
||||||
class LevelRenderer():
|
class LevelRenderer():
|
||||||
|
@ -21,11 +21,6 @@ class LevelRenderer():
|
||||||
def get_filename(self, mode, filetype, level=None):
|
def get_filename(self, mode, filetype, level=None):
|
||||||
return get_render_path(filetype, self.level.name if level is None else level, mode, self.only_public)
|
return get_render_path(filetype, self.level.name if level is None else level, mode, self.only_public)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_dimensions():
|
|
||||||
width, height = get_dimensions()
|
|
||||||
return (width * settings.RENDER_SCALE, height * settings.RENDER_SCALE)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def polygon_svg(geometry, fill_color=None, fill_opacity=None,
|
def polygon_svg(geometry, fill_color=None, fill_opacity=None,
|
||||||
stroke_width=0.0, stroke_color=None, stroke_opacity=None):
|
stroke_width=0.0, stroke_color=None, stroke_opacity=None):
|
||||||
|
@ -62,7 +57,7 @@ class LevelRenderer():
|
||||||
return element
|
return element
|
||||||
|
|
||||||
def create_svg(self):
|
def create_svg(self):
|
||||||
width, height = self.get_dimensions()
|
width, height = get_render_dimensions()
|
||||||
svg = ET.Element('svg', {
|
svg = ET.Element('svg', {
|
||||||
'width': str(width),
|
'width': str(width),
|
||||||
'height': str(height),
|
'height': str(height),
|
||||||
|
@ -73,7 +68,7 @@ class LevelRenderer():
|
||||||
return svg
|
return svg
|
||||||
|
|
||||||
def add_svg_content(self, svg):
|
def add_svg_content(self, svg):
|
||||||
width, height = self.get_dimensions()
|
width, height = get_render_dimensions()
|
||||||
contents = ET.Element('g', {
|
contents = ET.Element('g', {
|
||||||
'transform': 'scale(1 -1) translate(0 -%d)' % (height),
|
'transform': 'scale(1 -1) translate(0 -%d)' % (height),
|
||||||
})
|
})
|
||||||
|
@ -81,7 +76,7 @@ class LevelRenderer():
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
def add_svg_image(self, svg, image):
|
def add_svg_image(self, svg, image):
|
||||||
width, height = self.get_dimensions()
|
width, height = get_render_dimensions()
|
||||||
contents = ET.Element('image', {
|
contents = ET.Element('image', {
|
||||||
'x': '0',
|
'x': '0',
|
||||||
'y': '0',
|
'y': '0',
|
||||||
|
|
|
@ -4,8 +4,10 @@ from django.conf import settings
|
||||||
from django.db.models import Max, Min
|
from django.db.models import Max, Min
|
||||||
|
|
||||||
from c3nav.mapdata.models import Package
|
from c3nav.mapdata.models import Package
|
||||||
|
from c3nav.mapdata.utils.cache import cache_result
|
||||||
|
|
||||||
|
|
||||||
|
@cache_result('c3nav__mapdata__dimensions')
|
||||||
def get_dimensions():
|
def get_dimensions():
|
||||||
aggregate = Package.objects.all().aggregate(Max('right'), Min('left'), Max('top'), Min('bottom'))
|
aggregate = Package.objects.all().aggregate(Max('right'), Min('left'), Max('top'), Min('bottom'))
|
||||||
return (
|
return (
|
||||||
|
@ -14,6 +16,12 @@ def get_dimensions():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@cache_result('c3nav__mapdata__render_dimensions')
|
||||||
|
def get_render_dimensions():
|
||||||
|
width, height = get_dimensions()
|
||||||
|
return (width * settings.RENDER_SCALE, height * settings.RENDER_SCALE)
|
||||||
|
|
||||||
|
|
||||||
def get_render_path(filetype, level, mode, public):
|
def get_render_path(filetype, level, mode, public):
|
||||||
return os.path.join(settings.RENDER_ROOT,
|
return os.path.join(settings.RENDER_ROOT,
|
||||||
'%s%s-level-%s.%s' % (('public-' if public else ''), mode, level, filetype))
|
'%s%s-level-%s.%s' % (('public-' if public else ''), mode, level, filetype))
|
|
@ -6,3 +6,6 @@ class GraphConnection():
|
||||||
self.from_point = from_point
|
self.from_point = from_point
|
||||||
self.to_point = to_point
|
self.to_point = to_point
|
||||||
self.distance = distance if distance is not None else abs(np.linalg.norm(from_point.xy - to_point.xy))
|
self.distance = distance if distance is not None else abs(np.linalg.norm(from_point.xy - to_point.xy))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<GraphConnection %r %r distance=%f>' % (self.from_point, self.to_point, self.distance)
|
||||||
|
|
|
@ -12,7 +12,8 @@ from c3nav.mapdata.models.geometry import LevelConnector
|
||||||
from c3nav.mapdata.models.locations import AreaLocation, Location, LocationGroup, PointLocation
|
from c3nav.mapdata.models.locations import AreaLocation, Location, LocationGroup, PointLocation
|
||||||
from c3nav.routing.level import GraphLevel
|
from c3nav.routing.level import GraphLevel
|
||||||
from c3nav.routing.point import GraphPoint
|
from c3nav.routing.point import GraphPoint
|
||||||
from c3nav.routing.route import GraphRouteSegment, LevelRouteSegment, NoRoute, RoomRouteSegment, SegmentRoute
|
from c3nav.routing.route import NoRoute
|
||||||
|
from c3nav.routing.routesegments import GraphRouteSegment, LevelRouteSegment, RoomRouteSegment, SegmentRoute
|
||||||
|
|
||||||
|
|
||||||
class Graph:
|
class Graph:
|
||||||
|
|
|
@ -15,6 +15,10 @@ class GraphPoint():
|
||||||
self.connections = {}
|
self.connections = {}
|
||||||
self.connections_in = {}
|
self.connections_in = {}
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def level(self):
|
||||||
|
return self.room and self.room.level
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
return (
|
return (
|
||||||
self.x,
|
self.x,
|
||||||
|
@ -32,3 +36,6 @@ class GraphPoint():
|
||||||
connection = GraphConnection(self, other_point)
|
connection = GraphConnection(self, other_point)
|
||||||
self.connections[other_point] = connection
|
self.connections[other_point] = connection
|
||||||
other_point.connections_in[self] = connection
|
other_point.connections_in[self] = connection
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<GraphPoint x=%f y=%f room=%s>' % (self.x, self.y, (id(self.room) if self.room else None))
|
||||||
|
|
|
@ -1,139 +1,7 @@
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
|
from c3nav.mapdata.utils.misc import get_dimensions
|
||||||
class RouteSegment(ABC):
|
|
||||||
def __init__(self, routers, router, from_point, to_point):
|
|
||||||
"""
|
|
||||||
:param router: a Router (RoomRouter, GraphRouter, …)
|
|
||||||
:param from_point: in-router index of first point
|
|
||||||
:param to_point: in-router index of last point
|
|
||||||
"""
|
|
||||||
self.routers = routers
|
|
||||||
self.router = router
|
|
||||||
self.from_point = int(from_point)
|
|
||||||
self.to_point = int(to_point)
|
|
||||||
|
|
||||||
def as_route(self):
|
|
||||||
return SegmentRoute([self])
|
|
||||||
|
|
||||||
def _get_points(self):
|
|
||||||
points = [self.to_point]
|
|
||||||
first = self.from_point
|
|
||||||
current = self.to_point
|
|
||||||
while current != first:
|
|
||||||
current = self.router.predecessors[first, current]
|
|
||||||
points.append(current)
|
|
||||||
return tuple(reversed(points))
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def get_connections(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def distance(self):
|
|
||||||
return self.router.shortest_paths[self.from_point, self.to_point]
|
|
||||||
|
|
||||||
|
|
||||||
class RoomRouteSegment(RouteSegment):
|
|
||||||
def __init__(self, room, routers, from_point, to_point):
|
|
||||||
"""
|
|
||||||
Route segment within a Room
|
|
||||||
:param room: GraphRoom
|
|
||||||
"""
|
|
||||||
super().__init__(routers, routers[room], from_point, to_point)
|
|
||||||
self.room = room
|
|
||||||
self.global_from_point = room.points[from_point]
|
|
||||||
self.global_to_point = room.points[to_point]
|
|
||||||
|
|
||||||
def get_connections(self):
|
|
||||||
points = self._get_points()
|
|
||||||
return tuple(self.room.get_connection(from_point, to_point)
|
|
||||||
for from_point, to_point in zip(points[:-1], points[1:]))
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return ('<RoomRouteSegment in %r from points %d to %d with distance %f>' %
|
|
||||||
(self.room, self.from_point, self.to_point, self.distance))
|
|
||||||
|
|
||||||
|
|
||||||
class LevelRouteSegment(RouteSegment):
|
|
||||||
def __init__(self, level, routers, from_point, to_point):
|
|
||||||
"""
|
|
||||||
Route segment within a Level (from room transfer point to room transfer point)
|
|
||||||
:param level: GraphLevel
|
|
||||||
"""
|
|
||||||
super().__init__(routers, routers[level], from_point, to_point)
|
|
||||||
self.level = level
|
|
||||||
self.global_from_point = level.room_transfer_points[from_point]
|
|
||||||
self.global_to_point = level.room_transfer_points[to_point]
|
|
||||||
|
|
||||||
def split(self):
|
|
||||||
segments = []
|
|
||||||
points = self._get_points()
|
|
||||||
for from_point, to_point in zip(points[:-1], points[1:]):
|
|
||||||
room = self.level.rooms[self.router.room_transfers[from_point, to_point]]
|
|
||||||
global_from_point = self.level.room_transfer_points[from_point]
|
|
||||||
global_to_point = self.level.room_transfer_points[to_point]
|
|
||||||
segments.append(RoomRouteSegment(room, self.routers,
|
|
||||||
from_point=room.points.index(global_from_point),
|
|
||||||
to_point=room.points.index(global_to_point)))
|
|
||||||
return tuple(segments)
|
|
||||||
|
|
||||||
def get_connections(self):
|
|
||||||
return sum((segment.get_connections() for segment in self.split()), ())
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return ('<LevelRouteSegment in %r from points %d to %d with distance %f>' %
|
|
||||||
(self.level, self.from_point, self.to_point, self.distance))
|
|
||||||
|
|
||||||
|
|
||||||
class GraphRouteSegment(RouteSegment):
|
|
||||||
def __init__(self, graph, routers, from_point, to_point):
|
|
||||||
"""
|
|
||||||
Route segment within a Graph (from level transfer point to level transfer point)
|
|
||||||
:param graph: Graph
|
|
||||||
"""
|
|
||||||
super().__init__(routers, routers[graph], from_point, to_point)
|
|
||||||
self.graph = graph
|
|
||||||
self.global_from_point = graph.level_transfer_points[from_point]
|
|
||||||
self.global_to_point = graph.level_transfer_points[to_point]
|
|
||||||
|
|
||||||
def split(self):
|
|
||||||
segments = []
|
|
||||||
points = self._get_points()
|
|
||||||
for from_point, to_point in zip(points[:-1], points[1:]):
|
|
||||||
level = self.graph.levels[self.router.level_transfers[from_point, to_point]]
|
|
||||||
global_from_point = self.graph.level_transfer_points[from_point]
|
|
||||||
global_to_point = self.graph.level_transfer_points[to_point]
|
|
||||||
segments.append(LevelRouteSegment(level, self.routers,
|
|
||||||
from_point=level.room_transfer_points.index(global_from_point),
|
|
||||||
to_point=level.room_transfer_points.index(global_to_point)))
|
|
||||||
return tuple(segments)
|
|
||||||
|
|
||||||
def get_connections(self):
|
|
||||||
return sum((segment.get_connections() for segment in self.split()), ())
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return ('<GraphRouteSegment in %r from points %d to %d with distance %f>' %
|
|
||||||
(self.graph, self.from_point, self.to_point, self.distance))
|
|
||||||
|
|
||||||
|
|
||||||
class SegmentRoute:
|
|
||||||
def __init__(self, segments, distance=None):
|
|
||||||
self.segments = sum(((item.segments if isinstance(item, SegmentRoute) else (item,))
|
|
||||||
for item in segments if item.from_point != item.to_point), ())
|
|
||||||
self.distance = sum(segment.distance for segment in self.segments)
|
|
||||||
self.from_point = segments[0].global_from_point
|
|
||||||
self.to_point = segments[-1].global_to_point
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return ('<SegmentedRoute (\n %s\n) distance=%f>' %
|
|
||||||
('\n '.join(repr(segment) for segment in self.segments), self.distance))
|
|
||||||
|
|
||||||
def split(self):
|
|
||||||
return Route(sum((segment.get_connections() for segment in self.segments), ()))
|
|
||||||
|
|
||||||
|
|
||||||
class Route:
|
class Route:
|
||||||
|
@ -147,6 +15,73 @@ class Route:
|
||||||
return ('<Route (\n %s\n) distance=%f>' %
|
return ('<Route (\n %s\n) distance=%f>' %
|
||||||
('\n '.join(repr(connection) for connection in self.connections), self.distance))
|
('\n '.join(repr(connection) for connection in self.connections), self.distance))
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def routeparts(self):
|
||||||
|
routeparts = []
|
||||||
|
connections = []
|
||||||
|
level = self.connections[0].from_point.level
|
||||||
|
|
||||||
|
for connection in self.connections:
|
||||||
|
connections.append(connection)
|
||||||
|
point = connection.to_point
|
||||||
|
if point.level and point.level != level:
|
||||||
|
routeparts.append(RoutePart(level, connections))
|
||||||
|
level = point.level
|
||||||
|
connections = []
|
||||||
|
|
||||||
|
if connections:
|
||||||
|
routeparts.append(RoutePart(level, connections))
|
||||||
|
print(routeparts)
|
||||||
|
return tuple(routeparts)
|
||||||
|
|
||||||
|
|
||||||
|
class RoutePart:
|
||||||
|
def __init__(self, level, connections):
|
||||||
|
self.level = level
|
||||||
|
self.level_name = level.level.name
|
||||||
|
self.connections = connections
|
||||||
|
|
||||||
|
width, height = get_dimensions()
|
||||||
|
|
||||||
|
points = (connections[0].from_point, ) + tuple(connection.to_point for connection in connections)
|
||||||
|
for point in points:
|
||||||
|
point.svg_x = point.x * 6
|
||||||
|
point.svg_y = (height - point.y) * 6
|
||||||
|
|
||||||
|
x, y = zip(*((point.svg_x, point.svg_y) for point in points if point.level == level))
|
||||||
|
|
||||||
|
self.distance = sum(connection.distance for connection in connections)
|
||||||
|
|
||||||
|
# bounds for rendering
|
||||||
|
self.min_x = min(x) - 20
|
||||||
|
self.max_x = max(x) + 20
|
||||||
|
self.min_y = min(y) - 20
|
||||||
|
self.max_y = max(y) + 20
|
||||||
|
|
||||||
|
width = self.max_x - self.min_x
|
||||||
|
height = self.max_y - self.min_y
|
||||||
|
|
||||||
|
if width < 150:
|
||||||
|
self.min_x -= (10 - width) / 2
|
||||||
|
self.max_x += (10 - width) / 2
|
||||||
|
|
||||||
|
if height < 150:
|
||||||
|
self.min_y -= (10 - height) / 2
|
||||||
|
self.max_y += (10 - height) / 2
|
||||||
|
|
||||||
|
self.width = self.max_x - self.min_x
|
||||||
|
self.height = self.max_y - self.min_y
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return repr(self.__dict__)
|
||||||
|
|
||||||
|
|
||||||
|
class RouteLine:
|
||||||
|
def __init__(self, from_point, to_point, distance):
|
||||||
|
self.from_point = from_point
|
||||||
|
self.to_point = to_point
|
||||||
|
self.distance = distance
|
||||||
|
|
||||||
|
|
||||||
class NoRoute:
|
class NoRoute:
|
||||||
distance = np.inf
|
distance = np.inf
|
||||||
|
|
137
src/c3nav/routing/routesegments.py
Normal file
137
src/c3nav/routing/routesegments.py
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
|
from c3nav.routing.route import Route
|
||||||
|
|
||||||
|
|
||||||
|
class RouteSegment(ABC):
|
||||||
|
def __init__(self, routers, router, from_point, to_point):
|
||||||
|
"""
|
||||||
|
:param router: a Router (RoomRouter, GraphRouter, …)
|
||||||
|
:param from_point: in-router index of first point
|
||||||
|
:param to_point: in-router index of last point
|
||||||
|
"""
|
||||||
|
self.routers = routers
|
||||||
|
self.router = router
|
||||||
|
self.from_point = int(from_point)
|
||||||
|
self.to_point = int(to_point)
|
||||||
|
|
||||||
|
def as_route(self):
|
||||||
|
return SegmentRoute([self])
|
||||||
|
|
||||||
|
def _get_points(self):
|
||||||
|
points = [self.to_point]
|
||||||
|
first = self.from_point
|
||||||
|
current = self.to_point
|
||||||
|
while current != first:
|
||||||
|
current = self.router.predecessors[first, current]
|
||||||
|
points.append(current)
|
||||||
|
return tuple(reversed(points))
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_connections(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def distance(self):
|
||||||
|
return self.router.shortest_paths[self.from_point, self.to_point]
|
||||||
|
|
||||||
|
|
||||||
|
class RoomRouteSegment(RouteSegment):
|
||||||
|
def __init__(self, room, routers, from_point, to_point):
|
||||||
|
"""
|
||||||
|
Route segment within a Room
|
||||||
|
:param room: GraphRoom
|
||||||
|
"""
|
||||||
|
super().__init__(routers, routers[room], from_point, to_point)
|
||||||
|
self.room = room
|
||||||
|
self.global_from_point = room.points[from_point]
|
||||||
|
self.global_to_point = room.points[to_point]
|
||||||
|
|
||||||
|
def get_connections(self):
|
||||||
|
points = self._get_points()
|
||||||
|
return tuple(self.room.get_connection(from_point, to_point)
|
||||||
|
for from_point, to_point in zip(points[:-1], points[1:]))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return ('<RoomRouteSegment in %r from points %d to %d with distance %f>' %
|
||||||
|
(self.room, self.from_point, self.to_point, self.distance))
|
||||||
|
|
||||||
|
|
||||||
|
class LevelRouteSegment(RouteSegment):
|
||||||
|
def __init__(self, level, routers, from_point, to_point):
|
||||||
|
"""
|
||||||
|
Route segment within a Level (from room transfer point to room transfer point)
|
||||||
|
:param level: GraphLevel
|
||||||
|
"""
|
||||||
|
super().__init__(routers, routers[level], from_point, to_point)
|
||||||
|
self.level = level
|
||||||
|
self.global_from_point = level.room_transfer_points[from_point]
|
||||||
|
self.global_to_point = level.room_transfer_points[to_point]
|
||||||
|
|
||||||
|
def split(self):
|
||||||
|
segments = []
|
||||||
|
points = self._get_points()
|
||||||
|
for from_point, to_point in zip(points[:-1], points[1:]):
|
||||||
|
room = self.level.rooms[self.router.room_transfers[from_point, to_point]]
|
||||||
|
global_from_point = self.level.room_transfer_points[from_point]
|
||||||
|
global_to_point = self.level.room_transfer_points[to_point]
|
||||||
|
segments.append(RoomRouteSegment(room, self.routers,
|
||||||
|
from_point=room.points.index(global_from_point),
|
||||||
|
to_point=room.points.index(global_to_point)))
|
||||||
|
return tuple(segments)
|
||||||
|
|
||||||
|
def get_connections(self):
|
||||||
|
return sum((segment.get_connections() for segment in self.split()), ())
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return ('<LevelRouteSegment in %r from points %d to %d with distance %f>' %
|
||||||
|
(self.level, self.from_point, self.to_point, self.distance))
|
||||||
|
|
||||||
|
|
||||||
|
class GraphRouteSegment(RouteSegment):
|
||||||
|
def __init__(self, graph, routers, from_point, to_point):
|
||||||
|
"""
|
||||||
|
Route segment within a Graph (from level transfer point to level transfer point)
|
||||||
|
:param graph: Graph
|
||||||
|
"""
|
||||||
|
super().__init__(routers, routers[graph], from_point, to_point)
|
||||||
|
self.graph = graph
|
||||||
|
self.global_from_point = graph.level_transfer_points[from_point]
|
||||||
|
self.global_to_point = graph.level_transfer_points[to_point]
|
||||||
|
|
||||||
|
def split(self):
|
||||||
|
segments = []
|
||||||
|
points = self._get_points()
|
||||||
|
for from_point, to_point in zip(points[:-1], points[1:]):
|
||||||
|
level = self.graph.levels[self.router.level_transfers[from_point, to_point]]
|
||||||
|
global_from_point = self.graph.level_transfer_points[from_point]
|
||||||
|
global_to_point = self.graph.level_transfer_points[to_point]
|
||||||
|
segments.append(LevelRouteSegment(level, self.routers,
|
||||||
|
from_point=level.room_transfer_points.index(global_from_point),
|
||||||
|
to_point=level.room_transfer_points.index(global_to_point)))
|
||||||
|
return tuple(segments)
|
||||||
|
|
||||||
|
def get_connections(self):
|
||||||
|
return sum((segment.get_connections() for segment in self.split()), ())
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return ('<GraphRouteSegment in %r from points %d to %d with distance %f>' %
|
||||||
|
(self.graph, self.from_point, self.to_point, self.distance))
|
||||||
|
|
||||||
|
|
||||||
|
class SegmentRoute:
|
||||||
|
def __init__(self, segments, distance=None):
|
||||||
|
self.segments = sum(((item.segments if isinstance(item, SegmentRoute) else (item,))
|
||||||
|
for item in segments if item.from_point != item.to_point), ())
|
||||||
|
self.distance = sum(segment.distance for segment in self.segments)
|
||||||
|
self.from_point = segments[0].global_from_point
|
||||||
|
self.to_point = segments[-1].global_to_point
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return ('<SegmentedRoute (\n %s\n) distance=%f>' %
|
||||||
|
('\n '.join(repr(segment) for segment in self.segments), self.distance))
|
||||||
|
|
||||||
|
def split(self):
|
||||||
|
return Route(sum((segment.get_connections() for segment in self.segments), ()))
|
|
@ -90,3 +90,22 @@ body {
|
||||||
.tt-suggestion p {
|
.tt-suggestion p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width:100%;
|
||||||
|
height:auto;
|
||||||
|
vertical-align:bottom;
|
||||||
|
}
|
||||||
|
line {
|
||||||
|
stroke:#FF0000;
|
||||||
|
stroke-width:2.5px;
|
||||||
|
}
|
||||||
|
marker path {
|
||||||
|
fill: #FF0000;
|
||||||
|
stroke: 0;
|
||||||
|
}
|
||||||
|
circle.pos {
|
||||||
|
fill:#3399FF;
|
||||||
|
stroke-width:10px;
|
||||||
|
stroke:rgba(51, 153, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
{% load route_render %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form>
|
<form>
|
||||||
|
@ -12,6 +13,34 @@
|
||||||
{% trans "Destination" as heading %}
|
{% trans "Destination" as heading %}
|
||||||
{% include 'site/fragment_location.html' with name='destination' location=destination heading=heading %}
|
{% include 'site/fragment_location.html' with name='destination' location=destination heading=heading %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
{% if route %}
|
||||||
|
<h2>Your Route</h2>
|
||||||
|
<div class="routeparts">
|
||||||
|
{% for routepart in route.routeparts %}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="map" data-level="{{ routepart.level_name }}"
|
||||||
|
viewBox="0 0 {{ routepart.width }} {{ routepart.height }}"
|
||||||
|
style="max-height:{% if routepart.height > 300 %}300{% else %}{{ routepart.height }}{% endif %}px">
|
||||||
|
<defs>
|
||||||
|
<marker id="arrow-{{ forloop.counter0 }}" markerWidth="4" markerHeight="4" refX="2.5" refY="2" orient="auto">
|
||||||
|
<path d="M0,0 L3,2 L0,4 L0,0" style="fill: #FF0000; stroke: 0;"></path>
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<image width="{{ svg_width }}" height="{{ svg_height }}" x="{{ routepart.min_x | negate }}" y="{{ routepart.min_y | negate }}" xlink:href="/map/{{ routepart.level_name }}.png"></image>
|
||||||
|
|
||||||
|
<g class="connections">
|
||||||
|
{% for c in routepart.connections %}
|
||||||
|
<line x1="{{ c.from_point.svg_x | subtract:routepart.min_x }}"
|
||||||
|
y1="{{ c.from_point.svg_y | subtract:routepart.min_y }}"
|
||||||
|
x2="{{ c.to_point.svg_x | subtract:routepart.min_x }}"
|
||||||
|
y2="{{ c.to_point.svg_y | subtract:routepart.min_y }}"
|
||||||
|
marker-end="url(#arrow-{{ forloop.parentloop.counter0 }})"></line>
|
||||||
|
{% endfor %}
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
0
src/c3nav/site/templatetags/__init__.py
Normal file
0
src/c3nav/site/templatetags/__init__.py
Normal file
13
src/c3nav/site/templatetags/route_render.py
Normal file
13
src/c3nav/site/templatetags/route_render.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
from django import template
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def negate(value):
|
||||||
|
return -value
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def subtract(value, arg):
|
||||||
|
return value - arg
|
|
@ -1,8 +1,9 @@
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from c3nav.site.views import main
|
from c3nav.site.views import level_image, main
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
url(r'^map/(?P<level>[a-z0-9-_:]+).png$', level_image, name='site.level_image'),
|
||||||
url(r'^(?P<origin>[a-z0-9-_:]+)/$', main, name='site.main'),
|
url(r'^(?P<origin>[a-z0-9-_:]+)/$', main, name='site.main'),
|
||||||
url(r'^_/(?P<destination>[a-z0-9-_:]+)/$', main, name='site.main'),
|
url(r'^_/(?P<destination>[a-z0-9-_:]+)/$', main, name='site.main'),
|
||||||
url(r'^(?P<origin>[a-z0-9-_:]+)/(?P<destination>[a-z0-9-_:]+)/$', main, name='site.main'),
|
url(r'^(?P<origin>[a-z0-9-_:]+)/(?P<destination>[a-z0-9-_:]+)/$', main, name='site.main'),
|
||||||
|
|
|
@ -1,7 +1,16 @@
|
||||||
from django.shortcuts import redirect, render
|
import os
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
|
||||||
|
from c3nav.mapdata.models import Level
|
||||||
from c3nav.mapdata.models.locations import get_location
|
from c3nav.mapdata.models.locations import get_location
|
||||||
|
from c3nav.mapdata.render.compose import composer
|
||||||
|
from c3nav.mapdata.utils.misc import get_dimensions
|
||||||
from c3nav.routing.graph import Graph
|
from c3nav.routing.graph import Graph
|
||||||
|
from c3nav.routing.utils.draw import _line_coords
|
||||||
|
|
||||||
|
|
||||||
def main(request, origin=None, destination=None):
|
def main(request, origin=None, destination=None):
|
||||||
|
@ -30,13 +39,39 @@ def main(request, origin=None, destination=None):
|
||||||
|
|
||||||
redirect(new_url)
|
redirect(new_url)
|
||||||
|
|
||||||
|
route = None
|
||||||
if origin and destination:
|
if origin and destination:
|
||||||
graph = Graph.load()
|
graph = Graph.load()
|
||||||
route = graph.get_route(origin, destination)
|
route = graph.get_route(origin, destination)
|
||||||
|
route = route.split()
|
||||||
|
print(route)
|
||||||
|
|
||||||
print(route)
|
if False:
|
||||||
|
filename = os.path.join(settings.RENDER_ROOT, 'base-level-0.png')
|
||||||
|
|
||||||
|
im = Image.open(filename)
|
||||||
|
height = im.size[1]
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
for connection in route.connections:
|
||||||
|
draw.line(_line_coords(connection.from_point, connection.to_point, height), fill=(255, 100, 100))
|
||||||
|
|
||||||
|
response = HttpResponse(content_type="image/png")
|
||||||
|
im.save(response, "PNG")
|
||||||
|
return response
|
||||||
|
|
||||||
|
width, height = get_dimensions()
|
||||||
|
|
||||||
return render(request, 'site/main.html', {
|
return render(request, 'site/main.html', {
|
||||||
'origin': origin,
|
'origin': origin,
|
||||||
'destination': destination
|
'destination': destination,
|
||||||
|
'route': route,
|
||||||
|
'width': width,
|
||||||
|
'height': height,
|
||||||
|
'svg_width': width*6,
|
||||||
|
'svg_height': height*6,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def level_image(request, level):
|
||||||
|
level = get_object_or_404(Level, name=level, intermediate=False)
|
||||||
|
return composer.get_level_image(request, level)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue