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
|
2016-11-26 13:48:44 +01:00
|
|
|
from django.utils.functional import cached_property
|
2016-08-29 18:49:24 +02:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
2017-05-13 14:59:59 +02:00
|
|
|
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
|
|
|
|
2016-10-12 15:25:00 +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
|
|
|
|
2016-10-12 15:25:00 +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
|
|
|
|
2016-12-13 19:16:51 +01:00
|
|
|
@cached_property
|
|
|
|
def public_geometries(self):
|
2017-05-07 12:06:13 +02:00
|
|
|
return SectionGeometries.by_section(self, only_public=True)
|
2016-12-13 19:16:51 +01:00
|
|
|
|
2016-12-04 01:49:49 +01:00
|
|
|
@cached_property
|
|
|
|
def geometries(self):
|
2017-05-07 12:06:13 +02:00
|
|
|
return SectionGeometries.by_section(self, only_public=False)
|
2016-11-26 13:48:44 +01:00
|
|
|
|
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)
|
|
|
|
for s in level_spaces))
|
|
|
|
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 14:59:59 +02:00
|
|
|
hole_svg = svg.add_geometry(hole_geometries, defid='holes')
|
2017-05-12 23:37:03 +02:00
|
|
|
hole_mask = svg.add_mask(hole_svg, inverted=True, defid='holes-mask')
|
|
|
|
|
|
|
|
space_lower_svg = svg.add_geometry(space_geometries['lower'], defid='spaces-lower')
|
|
|
|
svg.use_geometry(space_lower_svg, fill_color='#d1d1d1')
|
|
|
|
|
2017-05-13 14:59:59 +02:00
|
|
|
door_geometries = cascaded_union(tuple(d.geometry for d in self.doors.all()))
|
|
|
|
section_geometry = cascaded_union((space_geometries[''], building_geometries, door_geometries))
|
|
|
|
section_svg = svg.add_geometry(section_geometry, defid='section')
|
|
|
|
svg.use_geometry(section_svg, fill_color='#d1d1d1', mask=hole_mask)
|
|
|
|
|
|
|
|
wall_geometry = building_geometries.difference(space_geometries['']).difference(door_geometries)
|
|
|
|
wall_svg = svg.add_geometry(wall_geometry, 'walls')
|
|
|
|
svg.use_geometry(wall_svg, fill_color='#929292')
|
2017-05-12 23:37:03 +02:00
|
|
|
|
2017-05-13 14:59:59 +02:00
|
|
|
accessible_mask = svg.add_mask(section_svg, wall_svg, hole_svg, subtract=True, defid='accessible')
|
2017-05-12 23:37:03 +02:00
|
|
|
|
2017-05-13 14:59:59 +02:00
|
|
|
wall_dilated_geometry = wall_geometry.buffer(0.7, join_style=JOIN_STYLE.mitre)
|
|
|
|
wall_dilated_svg = svg.add_geometry(wall_dilated_geometry, 'wall-shadows')
|
|
|
|
svg.use_geometry(wall_dilated_svg, fill_color='rgba(0, 0, 0, 0.1)', mask=accessible_mask, filter='wallblur')
|
|
|
|
|
|
|
|
svg.use_geometry(wall_svg, stroke_color='#333333', stroke_width=0.07)
|
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[''])
|
|
|
|
door_svg = svg.add_geometry(door_geometries, defid='doors')
|
2017-05-13 14:59:59 +02:00
|
|
|
svg.use_geometry(door_svg, fill_color='#ffffff', stroke_color='#929292', stroke_width=0.07)
|
2017-05-12 23:37:03 +02:00
|
|
|
|
|
|
|
space_upper_svg = svg.add_geometry(space_geometries['upper'], defid='spaces-upper')
|
|
|
|
svg.use_geometry(space_upper_svg, fill_color='#d1d1d1')
|
2017-05-13 14:59:59 +02:00
|
|
|
|
2017-05-12 23:37:03 +02:00
|
|
|
return svg.get_xml()
|