From 6906e1540c1c64944cef3f84908607d00dc76074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Thu, 8 Dec 2016 18:12:07 +0100 Subject: [PATCH] buildgraph: add points for steps --- src/c3nav/mapdata/models/geometry.py | 78 ++++++++++++++-------------- src/c3nav/routing/connection.py | 2 +- src/c3nav/routing/point.py | 4 -- src/c3nav/routing/room.py | 50 +++++++++++++++--- 4 files changed, 83 insertions(+), 51 deletions(-) diff --git a/src/c3nav/mapdata/models/geometry.py b/src/c3nav/mapdata/models/geometry.py index d64c0b97..2dbc9737 100644 --- a/src/c3nav/mapdata/models/geometry.py +++ b/src/c3nav/mapdata/models/geometry.py @@ -103,6 +103,35 @@ class GeometryMapItemWithLevel(GeometryMapItem): return result +class LineGeometryMapItemWithLevel(GeometryMapItemWithLevel): + geomtype = 'polyline' + + class Meta: + abstract = True + + def to_geojson(self): + result = super().to_geojson() + original_geometry = result['geometry'] + draw = self.geometry.buffer(0.05, join_style=JOIN_STYLE.mitre, cap_style=CAP_STYLE.flat) + result['geometry'] = format_geojson(mapping(draw)) + result['original_geometry'] = original_geometry + return result + + def to_shadow_geojson(self): + shadow = self.geometry.parallel_offset(0.03, 'left', join_style=JOIN_STYLE.mitre) + shadow = shadow.buffer(0.019, join_style=JOIN_STYLE.mitre, cap_style=CAP_STYLE.flat) + return OrderedDict(( + ('type', 'Feature'), + ('properties', OrderedDict(( + ('type', 'shadow'), + ('original_type', self.__class__.__name__.lower()), + ('original_name', self.name), + ('level', self.level.name), + ))), + ('geometry', format_geojson(mapping(shadow), round=False)), + )) + + class Building(GeometryMapItemWithLevel): """ The outline of a building on a specific level @@ -139,6 +168,16 @@ class Outside(GeometryMapItemWithLevel): default_related_name = 'outsides' +class Stair(LineGeometryMapItemWithLevel): + """ + A stair + """ + class Meta: + verbose_name = _('Stair') + verbose_name_plural = _('Stairs') + default_related_name = 'stairs' + + class Obstacle(GeometryMapItemWithLevel): """ An obstacle @@ -277,42 +316,3 @@ class ElevatorLevel(GeometryMapItemWithLevel): result['elevator'] = self.elevator.name result['button'] = self.button return result - - -class LineGeometryMapItemWithLevel(GeometryMapItemWithLevel): - geomtype = 'polyline' - - class Meta: - abstract = True - - def to_geojson(self): - result = super().to_geojson() - original_geometry = result['geometry'] - draw = self.geometry.buffer(0.05, join_style=JOIN_STYLE.mitre, cap_style=CAP_STYLE.flat) - result['geometry'] = format_geojson(mapping(draw)) - result['original_geometry'] = original_geometry - return result - - def to_shadow_geojson(self): - shadow = self.geometry.parallel_offset(0.03, 'left', join_style=JOIN_STYLE.mitre) - shadow = shadow.buffer(0.019, join_style=JOIN_STYLE.mitre, cap_style=CAP_STYLE.flat) - return OrderedDict(( - ('type', 'Feature'), - ('properties', OrderedDict(( - ('type', 'shadow'), - ('original_type', self.__class__.__name__.lower()), - ('original_name', self.name), - ('level', self.level.name), - ))), - ('geometry', format_geojson(mapping(shadow), round=False)), - )) - - -class Stair(LineGeometryMapItemWithLevel): - """ - A stair - """ - class Meta: - verbose_name = _('Stair') - verbose_name_plural = _('Stairs') - default_related_name = 'stairs' diff --git a/src/c3nav/routing/connection.py b/src/c3nav/routing/connection.py index 3b268d71..01afcf38 100644 --- a/src/c3nav/routing/connection.py +++ b/src/c3nav/routing/connection.py @@ -6,7 +6,7 @@ class GraphConnection(): self.graph = graph self.from_point = from_point self.to_point = to_point - self.distance = distance if distance is not None else 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)) if to_point in from_point.connections: self.graph.connections.remove(from_point.connections[to_point]) diff --git a/src/c3nav/routing/point.py b/src/c3nav/routing/point.py index 4d7580aa..60c7d833 100644 --- a/src/c3nav/routing/point.py +++ b/src/c3nav/routing/point.py @@ -1,7 +1,6 @@ import numpy as np from django.conf import settings from django.utils.functional import cached_property -from matplotlib.path import Path class GraphPoint(): @@ -34,8 +33,5 @@ class GraphPoint(): y = self.y * settings.RENDER_SCALE return ((x-5, y-5), (x+5, y+5)) - def path_to(self, to_point): - return Path(np.vstack((self.xy, to_point.xy))) - def connect_to(self, to_point): self.graph.add_connection(self, to_point) diff --git a/src/c3nav/routing/room.py b/src/c3nav/routing/room.py index fd4c9a1e..57f1d7ad 100644 --- a/src/c3nav/routing/room.py +++ b/src/c3nav/routing/room.py @@ -1,11 +1,13 @@ from itertools import combinations, permutations +import numpy as np +from matplotlib.path import Path from shapely.geometry import CAP_STYLE, JOIN_STYLE, LineString -from c3nav.mapdata.utils.geometry import assert_multipolygon +from c3nav.mapdata.utils.geometry import assert_multilinestring, assert_multipolygon from c3nav.routing.point import GraphPoint from c3nav.routing.router import Router -from c3nav.routing.utils.coords import get_coords_angles +from c3nav.routing.utils.coords import coord_angle, get_coords_angles from c3nav.routing.utils.mpl import shapely_to_mpl @@ -30,6 +32,10 @@ class GraphRoom(): def prepare_build(self): self.mpl_clear = shapely_to_mpl(self.clear_geometry.buffer(0.01, join_style=JOIN_STYLE.mitre)) + self.mpl_stairs = () + for stair_line in self.level.level.geometries.stairs: + coords = tuple(stair_line.coords) + self.mpl_stairs += tuple((Path(part), coord_angle(*part)) for part in zip(coords[:-1], coords[1:])) def build_points(self): original_geometry = self.geometry @@ -75,10 +81,19 @@ class GraphRoom(): stairs_areas = stairs_areas.buffer(0.3, join_style=JOIN_STYLE.mitre, cap_style=CAP_STYLE.flat) stairs_areas = assert_multipolygon(stairs_areas.intersection(self.geometry)) for polygon in stairs_areas: - self._add_ring(polygon.exterior, want_left=True) - - for interior in polygon.interiors: - self._add_ring(interior, want_left=False) + for ring in (polygon.exterior, )+tuple(polygon.interiors): + print('#') + for linestring in assert_multilinestring(ring.intersection(self.clear_geometry)): + print(' -') + coords = tuple(linestring.coords) + start = 1 + for segment in zip(coords[:-1], coords[1:]): + print(' .') + path = Path(segment) + length = abs(np.linalg.norm(path.vertices[0] - path.vertices[1])) + for coord in tuple(path.interpolated(max(int(length / 1.0), 1)).vertices)[start:-1]: + self.add_point(coord) + start = 0 def _add_ring(self, geom, want_left): """ @@ -121,12 +136,33 @@ class GraphRoom(): return [point] def build_connections(self): + i = 0 for point1, point2 in combinations(self.points, 2): - path = point1.path_to(point2) + path = Path(np.vstack((point1.xy, point2.xy))) + + # lies within room if self.mpl_clear.intersects_path(path): continue + + # stair checker + angle = coord_angle(point1.xy, point2.xy) + valid = True + for stair_path, stair_angle in self.mpl_stairs: + if not path.intersects_path(stair_path): + continue + + angle_diff = ((stair_angle - angle + 180) % 360) - 180 + up = angle_diff < 0 # noqa + if not (70 < abs(angle_diff) < 110): + valid = False + break + + if not valid: + continue + point1.connect_to(point2) point2.connect_to(point1) + i += 1 def build_router(self): self.router.build(self.points)