From 8936fe5aea2844b0382e6b33714a794fe53beaf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Wed, 12 Dec 2018 19:37:24 +0100 Subject: [PATCH] columns can have an accessrestriction now --- .../0003_column_access_restriction.py | 19 +++++++++++++++ src/c3nav/mapdata/models/geometry/level.py | 2 +- src/c3nav/mapdata/models/geometry/space.py | 3 ++- src/c3nav/mapdata/render/geometry/level.py | 23 +++++++++++++++---- src/c3nav/routing/router.py | 18 +++++++++++++++ 5 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 src/c3nav/mapdata/migrations/0003_column_access_restriction.py diff --git a/src/c3nav/mapdata/migrations/0003_column_access_restriction.py b/src/c3nav/mapdata/migrations/0003_column_access_restriction.py new file mode 100644 index 00000000..4bbeaaad --- /dev/null +++ b/src/c3nav/mapdata/migrations/0003_column_access_restriction.py @@ -0,0 +1,19 @@ +# Generated by Django 2.1.4 on 2018-12-12 17:47 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('mapdata', '0002_fix_broken_spaces'), + ] + + operations = [ + migrations.AddField( + model_name='column', + name='access_restriction', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='columns', to='mapdata.AccessRestriction', verbose_name='Access Restriction'), + ), + ] diff --git a/src/c3nav/mapdata/models/geometry/level.py b/src/c3nav/mapdata/models/geometry/level.py index 52725f94..fc5a9633 100644 --- a/src/c3nav/mapdata/models/geometry/level.py +++ b/src/c3nav/mapdata/models/geometry/level.py @@ -206,7 +206,7 @@ class AltitudeArea(LevelGeometryMixin, models.Model): if space.outside: space.geometry = space.geometry.difference(buildings_geom) space_accessible = space.geometry.difference( - unary_union(tuple(c.geometry for c in space.columns.all()) + + unary_union(tuple(c.geometry for c in space.columns.all() if c.access_restriction_id is None) + tuple(o.geometry for o in space.obstacles.all()) + tuple(o.buffered_geometry for o in space.lineobstacles.all()) + tuple(h.geometry for h in space.holes.all())) diff --git a/src/c3nav/mapdata/models/geometry/space.py b/src/c3nav/mapdata/models/geometry/space.py index 5b950a80..413b384a 100644 --- a/src/c3nav/mapdata/models/geometry/space.py +++ b/src/c3nav/mapdata/models/geometry/space.py @@ -13,6 +13,7 @@ from shapely.geometry import CAP_STYLE, JOIN_STYLE, mapping from c3nav.mapdata.fields import GeometryField, I18nField, JSONField from c3nav.mapdata.grid import grid from c3nav.mapdata.models import Space +from c3nav.mapdata.models.access import AccessRestrictionMixin from c3nav.mapdata.models.base import SerializableMixin from c3nav.mapdata.models.geometry.base import GeometryMixin from c3nav.mapdata.models.locations import SpecificLocation @@ -97,7 +98,7 @@ class SpaceGeometryMixin(GeometryMixin): super().save(*args, **kwargs) -class Column(SpaceGeometryMixin, models.Model): +class Column(SpaceGeometryMixin, AccessRestrictionMixin, models.Model): """ An column in a space, also used to be able to create rooms within rooms. """ diff --git a/src/c3nav/mapdata/render/geometry/level.py b/src/c3nav/mapdata/render/geometry/level.py index 213b5244..23910063 100644 --- a/src/c3nav/mapdata/render/geometry/level.py +++ b/src/c3nav/mapdata/render/geometry/level.py @@ -74,7 +74,7 @@ class LevelGeometries: subtract = [] if space.outside: subtract.append(buildings_geom) - columns = [c.geometry for c in space.columns.all()] + columns = [c.geometry for c in space.columns.all() if c.access_restriction_id is None] if columns: subtract.extend(columns) if subtract: @@ -108,14 +108,15 @@ class LevelGeometries: obstacles = {} heightareas = {} for space in level.spaces.all(): + buffered = space.geometry.buffer(0.01).union(unary_union( + tuple(door.geometry for door in level.doors.all() if door.geometry.intersects(space.geometry)) + ).difference(walkable_spaces_geom)) + intersects = buildings_geom_prep.intersects(buffered) + access_restriction = space.access_restriction_id if access_restriction is not None: access_restriction_affected.setdefault(access_restriction, []).append(space.geometry) - buffered = space.geometry.buffer(0.01).union(unary_union( - tuple(door.geometry for door in level.doors.all() if door.geometry.intersects(space.geometry)) - ).difference(walkable_spaces_geom)) - intersects = buildings_geom_prep.intersects(buffered) if intersects: restricted_spaces_indoors.setdefault(access_restriction, []).append( buffered.intersection(buildings_geom) @@ -134,6 +135,18 @@ class LevelGeometries: access_restriction_affected.setdefault(access_restriction, []).append(area.geometry) colors.setdefault(area.get_color(), {}).setdefault(access_restriction, []).append(area.geometry) + for column in space.columns.all(): + access_restriction = column.access_restriction_id + if access_restriction is None: + continue + column.geometry = column.geometry.intersection(space.walkable_geom) + buffered_column = column.geometry.buffer(0.01) + if intersects: + restricted_spaces_indoors.setdefault(access_restriction, []).append(buffered_column) + if not intersects or not buildings_geom_prep.contains(buffered): + restricted_spaces_outdoors.setdefault(access_restriction, []).append(buffered_column) + access_restriction_affected.setdefault(access_restriction, []).append(column.geometry) + for obstacle in space.obstacles.all(): if not obstacle.height: continue diff --git a/src/c3nav/routing/router.py b/src/c3nav/routing/router.py index db6d54ad..5cf0d164 100644 --- a/src/c3nav/routing/router.py +++ b/src/c3nav/routing/router.py @@ -176,6 +176,15 @@ class Router: pois[poi.pk] = poi space.pois.add(poi.pk) + for column in space_obj.columns.all(): + if column.access_restriction_id is None: + continue + column.geometry_prep = prepared.prep(column.geometry) + column_nodes = tuple(node for node in space_nodes if column.geometry_prep.intersects(node.point)) + column_nodes = set(node.i for node in column_nodes) + restrictions.setdefault(column.access_restriction_id, + RouterRestriction()).additional_nodes.update(column_nodes) + space_obj._prefetched_objects_cache = {} space.src.geometry = accessible_geom @@ -397,6 +406,9 @@ class Router: space_nodes = tuple(reduce(operator.or_, (self.spaces[space].nodes for space in restrictions.spaces), set())) graph[space_nodes, :] = np.inf graph[:, space_nodes] = np.inf + if restrictions.additional_nodes: + graph[restrictions.additional_nodes, :] = np.inf + graph[:, restrictions.additional_nodes] = np.inf graph[restrictions.edges.transpose().tolist()] = np.inf distances, predecessors = shortest_path(graph, directed=True, return_predecessors=True) @@ -670,6 +682,7 @@ class RouterLocation: class RouterRestriction: def __init__(self, spaces=None): self.spaces = spaces if spaces else set() + self.additional_nodes = set() self.edges = deque() @@ -681,6 +694,11 @@ class RouterRestrictionSet: def spaces(self): return reduce(operator.or_, (restriction.spaces for restriction in self.restrictions.values()), frozenset()) + @cached_property + def additional_nodes(self): + return reduce(operator.or_, (restriction.additional_nodes + for restriction in self.restrictions.values()), frozenset()) + @cached_property def edges(self): if not self.restrictions: