improve rendering speed by caching LevelGeometry in database
This commit is contained in:
parent
24977b80fe
commit
a9594b0cc5
5 changed files with 93 additions and 38 deletions
|
@ -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()
|
21
src/c3nav/mapdata/migrations/0037_level_geoms_cache.py
Normal file
21
src/c3nav/mapdata/migrations/0037_level_geoms_cache.py
Normal 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,
|
||||
),
|
||||
]
|
|
@ -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')
|
||||
|
|
50
src/c3nav/mapdata/render/base.py
Normal file
50
src/c3nav/mapdata/render/base.py
Normal 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
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue