team-3/src/c3nav/mapdata/models/section.py

111 lines
4.8 KiB
Python
Raw Normal View History

2017-05-12 23:37:03 +02:00
from django.conf import settings
2016-08-29 18:49:24 +02:00
from django.db import models
from django.utils.translation import ugettext_lazy as _
from shapely.geometry import JOIN_STYLE
2016-11-26 13:48:44 +01:00
from shapely.ops import cascaded_union
2016-08-29 18:49:24 +02:00
2017-05-08 16:40:22 +02:00
from c3nav.mapdata.models.base import EditorFormMixin
2017-05-10 18:03:57 +02:00
from c3nav.mapdata.models.locations import SpecificLocation
2017-05-12 23:37:03 +02:00
from c3nav.mapdata.render.svg import SVGImage
from c3nav.mapdata.utils.misc import get_dimensions
2016-08-29 18:49:24 +02:00
2017-05-10 18:03:57 +02:00
class Section(SpecificLocation, EditorFormMixin, models.Model):
2016-08-29 18:49:24 +02:00
"""
2017-05-07 12:06:13 +02:00
A map section like a level
2016-08-29 18:49:24 +02:00
"""
2017-05-07 12:06:13 +02:00
name = models.SlugField(_('section name'), unique=True, max_length=50)
altitude = models.DecimalField(_('section altitude'), null=False, unique=True, max_digits=6, decimal_places=2)
2016-08-29 18:49:24 +02:00
class Meta:
2017-05-07 12:06:13 +02:00
verbose_name = _('Section')
verbose_name_plural = _('Sections')
default_related_name = 'sections'
2016-12-05 12:14:07 +01:00
ordering = ['altitude']
2016-09-24 14:09:52 +02:00
2016-11-26 13:48:44 +01:00
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
2016-12-04 01:49:49 +01:00
def lower(self):
2017-05-07 12:06:13 +02:00
return Section.objects.filter(altitude__lt=self.altitude).order_by('altitude')
2016-12-04 01:49:49 +01:00
def higher(self):
2017-05-07 12:06:13 +02:00
return Section.objects.filter(altitude__gt=self.altitude).order_by('altitude')
2016-12-04 01:49:49 +01:00
2017-05-11 19:36:49 +02:00
def _serialize(self, section=True, **kwargs):
result = super()._serialize(**kwargs)
result['name'] = self.name
result['altitude'] = float(str(self.altitude))
return result
2016-12-01 12:25:02 +01:00
2017-05-12 23:37:03 +02:00
def render_svg(self):
width, height = get_dimensions()
svg = SVGImage(width=width, height=height, scale=settings.RENDER_SCALE)
building_geometries = cascaded_union(tuple(b.geometry for b in self.buildings.all()))
spaces = self.spaces.all()
space_levels = {
'upper': [],
'lower': [],
'': [],
}
for space in spaces:
space_levels[space.level].append(space)
space_geometries = {
level: cascaded_union(tuple((s.geometry.difference(building_geometries) if s.outside else s.geometry)
2017-05-13 16:42:39 +02:00
for s in level_spaces))
2017-05-12 23:37:03 +02:00
for level, level_spaces in space_levels.items()}
hole_geometries = cascaded_union(tuple(h.geometry for h in self.holes.all()))
hole_geometries = hole_geometries.intersection(space_geometries[''])
2017-05-13 16:39:01 +02:00
lower_spaces_by_color = {}
for space in space_levels['lower']:
lower_spaces_by_color.setdefault(space.get_color(), []).append(space)
for i, (color, color_spaces) in enumerate(lower_spaces_by_color.items()):
geometries = cascaded_union(tuple(space.geometry for space in color_spaces))
2017-05-13 20:10:12 +02:00
svg.add_geometry(geometries, fill_color=color or '#d1d1d1')
2017-05-12 23:37:03 +02:00
2017-05-13 16:39:01 +02:00
# draw space background
door_geometries = cascaded_union(tuple(d.geometry for d in self.doors.all()))
section_geometry = cascaded_union((space_geometries[''], building_geometries, door_geometries))
2017-05-13 19:19:28 +02:00
section_geometry = section_geometry.difference(hole_geometries)
2017-05-13 20:10:12 +02:00
section_clip = svg.register_geometry(section_geometry, defid='section', as_clip_path=True)
svg.add_geometry(fill_color='#d1d1d1', clip_path=section_clip)
2017-05-13 16:39:01 +02:00
# color in spaces
spaces_by_color = {}
for space in space_levels['']:
spaces_by_color.setdefault(space.get_color(), []).append(space)
spaces_by_color.pop(None, None)
2017-05-13 20:48:48 +02:00
spaces_by_color.pop('', None)
2017-05-13 16:39:01 +02:00
for i, (color, color_spaces) in enumerate(spaces_by_color.items()):
geometries = cascaded_union(tuple(space.geometry for space in color_spaces))
2017-05-13 20:48:48 +02:00
svg.add_geometry(geometries, fill_color=color)
2017-05-13 16:39:01 +02:00
# calculate walls
wall_geometry = building_geometries.difference(space_geometries['']).difference(door_geometries)
2017-05-12 23:37:03 +02:00
2017-05-13 16:39:01 +02:00
# draw wall shadow
wall_dilated_geometry = wall_geometry.buffer(0.7, join_style=JOIN_STYLE.mitre)
2017-05-13 20:10:12 +02:00
svg.add_geometry(wall_dilated_geometry, fill_color='#000000', opacity=0.1, filter='wallblur', clip_path=section_clip)
2017-05-13 16:39:01 +02:00
# draw walls
2017-05-13 20:10:12 +02:00
svg.add_geometry(wall_geometry, fill_color='#929292', stroke_color='#333333', stroke_width=0.07)
2017-05-12 23:37:03 +02:00
2017-05-13 16:39:01 +02:00
# draw doors
2017-05-12 23:37:03 +02:00
door_geometries = cascaded_union(tuple(d.geometry for d in self.doors.all()))
door_geometries = door_geometries.difference(space_geometries[''])
2017-05-13 20:10:12 +02:00
svg.add_geometry(door_geometries, fill_color='#ffffff', stroke_color='#929292', stroke_width=0.07)
2017-05-12 23:37:03 +02:00
2017-05-13 16:39:01 +02:00
# draw upper spaces
upper_spaces_by_color = {}
for space in space_levels['upper']:
upper_spaces_by_color.setdefault(space.get_color(), []).append(space)
for i, (color, color_spaces) in enumerate(upper_spaces_by_color.items()):
geometries = cascaded_union(tuple(space.geometry for space in color_spaces))
2017-05-13 20:10:12 +02:00
svg.add_geometry(geometries, fill_color=color or '#d1d1d1')
2017-05-12 23:37:03 +02:00
return svg.get_xml()