improve rendering speed by caching LevelGeometry in database

This commit is contained in:
Laura Klünder 2017-10-19 13:35:17 +02:00
parent 24977b80fe
commit a9594b0cc5
5 changed files with 93 additions and 38 deletions

View file

@ -0,0 +1,9 @@
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = 'rebuild level geometries'
def handle(self, *args, **options):
from c3nav.mapdata.render.base import LevelGeometries
LevelGeometries.rebuild()

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-10-19 11:21
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mapdata', '0036_geometry_bounds'),
]
operations = [
migrations.AddField(
model_name='level',
name='geoms_cache',
field=models.BinaryField(default=b''),
preserve_default=False,
),
]

View file

@ -26,6 +26,8 @@ class Level(SpecificLocation, models.Model):
on_top_of = models.ForeignKey('mapdata.Level', null=True, on_delete=models.CASCADE,
related_name='levels_on_top', verbose_name=_('on top of'))
geoms_cache = models.BinaryField()
class Meta:
verbose_name = _('Level')
verbose_name_plural = _('Levels')

View file

@ -0,0 +1,50 @@
import pickle
from django.db import transaction
from django.db.models import Q
from shapely.ops import unary_union
from c3nav.mapdata.models import Level
class LevelGeometries:
def __init__(self):
self.altitudeareas = []
self.walls = None
self.doors = None
@staticmethod
def rebuild():
levels = Level.objects.prefetch_related('altitudeareas', 'buildings', 'doors', 'spaces',
'spaces__holes', 'spaces__columns')
for level in levels:
geoms = LevelGeometries()
buildings_geom = unary_union([b.geometry for b in level.buildings.all()])
for space in level.spaces.all():
if space.outside:
space.geometry = space.geometry.difference(buildings_geom)
space.geometry = space.geometry.difference(unary_union([c.geometry for c in space.columns.all()]))
space.holes_geom = unary_union([h.geometry for h in space.holes.all()])
space.walkable_geom = space.geometry.difference(space.holes_geom)
spaces_geom = unary_union([s.geometry for s in level.spaces.all()])
geoms.doors = unary_union([d.geometry for d in level.doors.all()])
walkable_geom = unary_union([s.walkable_geom for s in level.spaces.all()]).union(geoms.doors)
for altitudearea in level.altitudeareas.all():
geoms.altitudeareas.append((altitudearea.geometry.intersection(walkable_geom), altitudearea.altitude))
geoms.walls = buildings_geom.difference(spaces_geom).difference(geoms.doors)
level.geoms_cache = pickle.dumps(geoms)
level.save()
with transaction.atomic():
for level in levels:
level.save()
def get_render_level_data(level):
levels = Level.objects.filter(Q(on_top_of=level.pk) | Q(base_altitude__lte=level.base_altitude))
levels = levels.values_list('geoms_cache', 'default_height')
return levels

View file

@ -1,8 +1,8 @@
from django.db.models import Prefetch, Q
from shapely.geometry import box
from shapely.ops import unary_union
import pickle
from c3nav.mapdata.models import AltitudeArea, Building, Door, Level, Space
from shapely.geometry import box
from c3nav.mapdata.render.base import get_render_level_data
from c3nav.mapdata.utils.svg import SVGImage
@ -12,41 +12,14 @@ def render_svg(level, miny, minx, maxy, maxx, scale=1):
within_coords = (minx-2, miny-2, maxx+2, maxy+2)
bbox = box(*within_coords)
levels = Level.objects.filter(Q(on_top_of=level.pk) | Q(base_altitude__lte=level.base_altitude))
levels = levels.prefetch_related(Prefetch('altitudeareas', AltitudeArea.objects.within(*within_coords)),
Prefetch('buildings', Building.objects.within(*within_coords)),
Prefetch('doors', Door.objects.within(*within_coords)),
Prefetch('spaces', Space.objects.within(*within_coords).prefetch_related(
'holes', 'columns'
)))
for geoms_cache, default_height in get_render_level_data(level):
geoms = pickle.loads(geoms_cache)
for altitudearea_geom, altitude in geoms.altitudeareas:
svg.add_geometry(bbox.intersection(altitudearea_geom), fill_color='#eeeeee', altitude=altitude)
for level in levels:
buildings_geom = bbox.intersection(unary_union([b.geometry for b in level.buildings.all()]))
# svg.add_geometry(buildings_geom, fill_color='#aaaaaa')
svg.add_geometry(bbox.intersection(geoms.walls),
fill_color='#aaaaaa', stroke_px=0.5, stroke_color='#aaaaaa', elevation=default_height)
for space in level.spaces.all():
if space.outside:
space.geometry = space.geometry.difference(buildings_geom)
space.geometry = space.geometry.difference(unary_union([c.geometry for c in space.columns.all()]))
space.holes_geom = unary_union([h.geometry for h in space.holes.all()])
space.walkable_geom = space.geometry.difference(space.holes_geom)
spaces_geom = bbox.intersection(unary_union([s.geometry for s in level.spaces.all()]))
doors_geom = bbox.intersection(unary_union([d.geometry for d in level.doors.all()]))
walkable_geom = unary_union([w.walkable_geom for w in level.spaces.all()]).union(doors_geom)
for altitudearea in level.altitudeareas.all():
svg.add_geometry(bbox.intersection(altitudearea.geometry).intersection(walkable_geom),
fill_color='#eeeeee', altitude=altitudearea.altitude)
spaces_geom = bbox.intersection(unary_union([s.geometry for s in level.spaces.all()]))
doors_geom = bbox.intersection(unary_union([d.geometry for d in level.doors.all()]))
walls_geom = buildings_geom.difference(spaces_geom).difference(doors_geom)
svg.add_geometry(walls_geom, fill_color='#aaaaaa', stroke_px=0.5, stroke_color='#aaaaaa',
elevation=level.default_height)
svg.add_geometry(doors_geom, fill_color='#ffffff', elevation=0)
svg.add_geometry(bbox.intersection(geoms.doors), fill_color='#ffffff', elevation=0)
return svg