introduce intermediate level setting
This commit is contained in:
parent
a4a7f02d0b
commit
9446c34e8a
8 changed files with 39 additions and 15 deletions
|
@ -72,11 +72,16 @@ class LevelsForLevel:
|
||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
levels_under = ()
|
levels_under = ()
|
||||||
levels_on_top = ()
|
levels_on_top = ()
|
||||||
lower_level = level.lower(Level).first()
|
lower_levels = []
|
||||||
primary_levels = (level,) + ((lower_level,) if lower_level else ())
|
for sublevel in level.lower(Level):
|
||||||
|
lower_levels.append(sublevel)
|
||||||
|
if not sublevel.intermediate:
|
||||||
|
break
|
||||||
|
primary_levels = chain((level,), lower_levels)
|
||||||
secondary_levels = Level.objects.filter(on_top_of__in=primary_levels).values_list('pk', 'on_top_of')
|
secondary_levels = Level.objects.filter(on_top_of__in=primary_levels).values_list('pk', 'on_top_of')
|
||||||
if lower_level:
|
lower_level_pks = set(l.pk for l in lower_levels)
|
||||||
levels_under = tuple(pk for pk, on_top_of in secondary_levels if on_top_of == lower_level.pk)
|
if lower_levels:
|
||||||
|
levels_under = tuple(pk for pk, on_top_of in secondary_levels if on_top_of in lower_level_pks)
|
||||||
if True:
|
if True:
|
||||||
levels_on_top = tuple(pk for pk, on_top_of in secondary_levels if on_top_of == level.pk)
|
levels_on_top = tuple(pk for pk, on_top_of in secondary_levels if on_top_of == level.pk)
|
||||||
|
|
||||||
|
|
|
@ -401,7 +401,8 @@ def create_editor_form(editor_model):
|
||||||
'slug', 'name', 'title', 'title_plural', 'help_text', 'position_secret', 'icon', 'join_edges',
|
'slug', 'name', 'title', 'title_plural', 'help_text', 'position_secret', 'icon', 'join_edges',
|
||||||
'up_separate', 'bssid', 'main_point', 'external_url', 'hub_import_type', 'walk', 'ordering',
|
'up_separate', 'bssid', 'main_point', 'external_url', 'hub_import_type', 'walk', 'ordering',
|
||||||
'category', 'width', 'groups', 'height', 'color', 'in_legend', 'priority', 'hierarchy', 'icon_name',
|
'category', 'width', 'groups', 'height', 'color', 'in_legend', 'priority', 'hierarchy', 'icon_name',
|
||||||
'base_altitude', 'waytype', 'access_restriction', 'default_height', 'door_height', 'outside', 'can_search',
|
'base_altitude', 'intermediate',
|
||||||
|
'waytype', 'access_restriction', 'default_height', 'door_height', 'outside', 'can_search',
|
||||||
'can_describe', 'geometry', 'single', 'altitude', 'short_label', 'origin_space', 'target_space', 'data',
|
'can_describe', 'geometry', 'single', 'altitude', 'short_label', 'origin_space', 'target_space', 'data',
|
||||||
'comment', 'slow_down_factor', 'groundaltitude', 'node_number', 'wifi_bssid', 'bluetooth_address', "group",
|
'comment', 'slow_down_factor', 'groundaltitude', 'node_number', 'wifi_bssid', 'bluetooth_address', "group",
|
||||||
'ibeacon_uuid', 'ibeacon_major', 'ibeacon_minor', 'uwb_address', 'extra_seconds', 'speed', 'can_report_missing',
|
'ibeacon_uuid', 'ibeacon_major', 'ibeacon_minor', 'uwb_address', 'extra_seconds', 'speed', 'can_report_missing',
|
||||||
|
|
|
@ -25,6 +25,7 @@ class Level(SpecificLocation, models.Model):
|
||||||
validators=[MinValueValidator(Decimal('0'))])
|
validators=[MinValueValidator(Decimal('0'))])
|
||||||
on_top_of = models.ForeignKey('mapdata.Level', null=True, on_delete=models.CASCADE,
|
on_top_of = models.ForeignKey('mapdata.Level', null=True, on_delete=models.CASCADE,
|
||||||
related_name='levels_on_top', verbose_name=_('on top of'))
|
related_name='levels_on_top', verbose_name=_('on top of'))
|
||||||
|
intermediate = models.BooleanField(_("intermediate level"), default=False)
|
||||||
short_label = models.SlugField(max_length=20, verbose_name=_('short label'), unique=True)
|
short_label = models.SlugField(max_length=20, verbose_name=_('short label'), unique=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -64,7 +64,7 @@ class RenderEngine(ABC):
|
||||||
def add_group(self, group):
|
def add_group(self, group):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def darken(self, area):
|
def darken(self, area, much=False):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def add_geometry(self, geometry, fill: Optional[FillAttribs] = None, stroke: Optional[StrokeAttribs] = None,
|
def add_geometry(self, geometry, fill: Optional[FillAttribs] = None, stroke: Optional[StrokeAttribs] = None,
|
||||||
|
|
|
@ -232,9 +232,9 @@ class SVGEngine(RenderEngine):
|
||||||
else:
|
else:
|
||||||
self.altitudes[new_altitude] = new_geometry
|
self.altitudes[new_altitude] = new_geometry
|
||||||
|
|
||||||
def darken(self, area):
|
def darken(self, area, much=False):
|
||||||
if area:
|
if area:
|
||||||
self.add_geometry(geometry=area, fill=FillAttribs('#000000', 0.1), category='darken')
|
self.add_geometry(geometry=area, fill=FillAttribs('#000000', 0.5 if much else 0.1), category='darken')
|
||||||
|
|
||||||
def _add_geometry(self, geometry, fill: Optional[FillAttribs], stroke: Optional[StrokeAttribs],
|
def _add_geometry(self, geometry, fill: Optional[FillAttribs], stroke: Optional[StrokeAttribs],
|
||||||
altitude=None, height=None, shadow_color=None, shape_cache_key=None, **kwargs):
|
altitude=None, height=None, shadow_color=None, shape_cache_key=None, **kwargs):
|
||||||
|
|
|
@ -6,11 +6,11 @@ from functools import reduce
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from shapely import prepared
|
from shapely import prepared, box
|
||||||
from shapely.geometry import GeometryCollection, Polygon, MultiPolygon
|
from shapely.geometry import GeometryCollection, Polygon, MultiPolygon
|
||||||
from shapely.ops import unary_union
|
from shapely.ops import unary_union
|
||||||
|
|
||||||
from c3nav.mapdata.models import Space, Level, AltitudeArea
|
from c3nav.mapdata.models import Space, Level, AltitudeArea, Source
|
||||||
from c3nav.mapdata.render.geometry.altitudearea import AltitudeAreaGeometries
|
from c3nav.mapdata.render.geometry.altitudearea import AltitudeAreaGeometries
|
||||||
from c3nav.mapdata.render.geometry.hybrid import HybridGeometry
|
from c3nav.mapdata.render.geometry.hybrid import HybridGeometry
|
||||||
from c3nav.mapdata.render.geometry.mesh import Mesh
|
from c3nav.mapdata.render.geometry.mesh import Mesh
|
||||||
|
@ -294,6 +294,13 @@ class SingleLevelGeometries(BaseLevelGeometries):
|
||||||
walls_geom = buildings_geom.difference(unary_union((spaces_geom, doors_geom)))
|
walls_geom = buildings_geom.difference(unary_union((spaces_geom, doors_geom)))
|
||||||
if level.on_top_of_id is None:
|
if level.on_top_of_id is None:
|
||||||
holes_geom = unary_union([s.holes_geom for s in spaces])
|
holes_geom = unary_union([s.holes_geom for s in spaces])
|
||||||
|
|
||||||
|
if level.intermediate:
|
||||||
|
holes_geom = unary_union([
|
||||||
|
holes_geom,
|
||||||
|
box(*chain(*Source.max_bounds())).difference(buildings_geom).difference(spaces_geom)
|
||||||
|
])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
holes_geom = None
|
holes_geom = None
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ class LevelRenderData:
|
||||||
lowest_important_level: int
|
lowest_important_level: int
|
||||||
levels: list[CompositeLevelGeometries] = field(default_factory=list)
|
levels: list[CompositeLevelGeometries] = field(default_factory=list)
|
||||||
darken_area: MultiPolygon | None = None
|
darken_area: MultiPolygon | None = None
|
||||||
|
darken_much: bool = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def rebuild(update_cache_key):
|
def rebuild(update_cache_key):
|
||||||
|
@ -125,10 +126,11 @@ class LevelRenderData:
|
||||||
map_history = MapHistory.open_level(render_level.pk, 'base')
|
map_history = MapHistory.open_level(render_level.pk, 'base')
|
||||||
|
|
||||||
# collect potentially relevant levels for rendering this level
|
# collect potentially relevant levels for rendering this level
|
||||||
# these are all levels that are on_top_of this level or below this level
|
# these are all levels that are on_top_of this level or below this level (inless intermediate)
|
||||||
relevant_levels = tuple(
|
relevant_levels = tuple(
|
||||||
sublevel for sublevel in levels
|
sublevel for sublevel in levels
|
||||||
if sublevel.on_top_of_id == render_level.pk or sublevel.base_altitude <= render_level.base_altitude
|
if (sublevel.pk == render_level.pk or sublevel.on_top_of_id == render_level.pk or
|
||||||
|
(sublevel.base_altitude <= render_level.base_altitude and not sublevel.intermediate))
|
||||||
)
|
)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -168,7 +170,7 @@ class LevelRenderData:
|
||||||
upper_bounds[geoms.pk] = last_lower_bound
|
upper_bounds[geoms.pk] = last_lower_bound
|
||||||
last_lower_bound = geoms.lower_bound
|
last_lower_bound = geoms.lower_bound
|
||||||
|
|
||||||
# set crop area if we area on the second primary layer from top or below
|
# set crop area if we are on the second primary layer from top or below
|
||||||
level_crop_to[level.pk] = Cropper(crop_to if primary_level_count > 1 else None)
|
level_crop_to[level.pk] = Cropper(crop_to if primary_level_count > 1 else None)
|
||||||
|
|
||||||
if geoms.holes is not None: # there area holes on this area
|
if geoms.holes is not None: # there area holes on this area
|
||||||
|
@ -180,6 +182,10 @@ class LevelRenderData:
|
||||||
if crop_to.is_empty:
|
if crop_to.is_empty:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if render_level.intermediate:
|
||||||
|
# todo: would be nice to still have the staircases leading to this level i guess?
|
||||||
|
lowest_important_level = render_level
|
||||||
|
|
||||||
render_data = LevelRenderData(
|
render_data = LevelRenderData(
|
||||||
base_altitude=render_level.base_altitude,
|
base_altitude=render_level.base_altitude,
|
||||||
lowest_important_level=lowest_important_level.pk,
|
lowest_important_level=lowest_important_level.pk,
|
||||||
|
@ -201,6 +207,8 @@ class LevelRenderData:
|
||||||
|
|
||||||
if single_geoms.holes and render_data.darken_area is None and lowest_important_level_passed:
|
if single_geoms.holes and render_data.darken_area is None and lowest_important_level_passed:
|
||||||
render_data.darken_area = single_geoms.holes
|
render_data.darken_area = single_geoms.holes
|
||||||
|
if render_level.intermediate:
|
||||||
|
render_data.darken_much = True
|
||||||
|
|
||||||
if crop_to.geometry is not None:
|
if crop_to.geometry is not None:
|
||||||
map_history.composite(MapHistory.open_level(level.pk, 'base'), crop_to.geometry)
|
map_history.composite(MapHistory.open_level(level.pk, 'base'), crop_to.geometry)
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from shapely import prepared
|
from shapely import prepared
|
||||||
from shapely.geometry import box
|
from shapely.geometry import box
|
||||||
|
|
||||||
from c3nav.mapdata.models import Level
|
from c3nav.mapdata.models import Level, Source
|
||||||
from c3nav.mapdata.render.engines.base import FillAttribs, StrokeAttribs
|
from c3nav.mapdata.render.engines.base import FillAttribs, StrokeAttribs
|
||||||
from c3nav.mapdata.render.geometry import hybrid_union
|
from c3nav.mapdata.render.geometry import hybrid_union
|
||||||
from c3nav.mapdata.render.renderdata import LevelRenderData
|
from c3nav.mapdata.render.renderdata import LevelRenderData
|
||||||
|
@ -61,7 +63,7 @@ class MapRenderer:
|
||||||
engine.add_group('level_%s' % geoms.short_label)
|
engine.add_group('level_%s' % geoms.short_label)
|
||||||
|
|
||||||
if geoms.pk == level_render_data.lowest_important_level:
|
if geoms.pk == level_render_data.lowest_important_level:
|
||||||
engine.darken(level_render_data.darken_area)
|
engine.darken(level_render_data.darken_area, much=level_render_data.darken_much)
|
||||||
|
|
||||||
if not bbox.intersects(geoms.affected_area):
|
if not bbox.intersects(geoms.affected_area):
|
||||||
continue
|
continue
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue