rendermap: render a public and a non-public version of the map
This commit is contained in:
parent
9c202ab6cd
commit
237b89dcc8
4 changed files with 75 additions and 44 deletions
|
@ -26,9 +26,13 @@ class Level(MapItem):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def public_geometries(self):
|
||||||
|
return LevelGeometries.by_level(self, only_public=True)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def geometries(self):
|
def geometries(self):
|
||||||
return LevelGeometries.by_level(self)
|
return LevelGeometries.by_level(self, only_public=False)
|
||||||
|
|
||||||
def tofilename(self):
|
def tofilename(self):
|
||||||
return 'levels/%s.json' % self.name
|
return 'levels/%s.json' % self.name
|
||||||
|
@ -75,19 +79,29 @@ class LevelGeometries():
|
||||||
by_level_name = {}
|
by_level_name = {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def by_level(cls, level):
|
def by_level(cls, level, only_public=True):
|
||||||
return cls.by_level_name.setdefault(level.name, cls(level))
|
return cls.by_level_name.setdefault((level.name, only_public), cls(level, only_public=only_public))
|
||||||
|
|
||||||
def __init__(self, level):
|
def __init__(self, level, only_public=True):
|
||||||
self.level = level
|
self.level = level
|
||||||
|
self.only_public = only_public
|
||||||
|
|
||||||
|
from c3nav.mapdata.permissions import get_public_packages
|
||||||
|
self.public_packages = get_public_packages()
|
||||||
|
|
||||||
|
def query(self, name):
|
||||||
|
queryset = getattr(self.level, name)
|
||||||
|
if not self.only_public:
|
||||||
|
return queryset.all()
|
||||||
|
return queryset.filter(package__in=self.public_packages)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def raw_rooms(self):
|
def raw_rooms(self):
|
||||||
return cascaded_union([room.geometry for room in self.level.rooms.all()])
|
return cascaded_union([room.geometry for room in self.query('rooms')])
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def buildings(self):
|
def buildings(self):
|
||||||
result = cascaded_union([building.geometry for building in self.level.buildings.all()])
|
result = cascaded_union([building.geometry for building in self.query('buildings')])
|
||||||
if self.level.intermediate:
|
if self.level.intermediate:
|
||||||
result = cascaded_union([result, self.raw_rooms])
|
result = cascaded_union([result, self.raw_rooms])
|
||||||
return result
|
return result
|
||||||
|
@ -98,7 +112,7 @@ class LevelGeometries():
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def outsides(self):
|
def outsides(self):
|
||||||
return cascaded_union([outside.geometry for outside in self.level.outsides.all()]).difference(self.buildings)
|
return cascaded_union([outside.geometry for outside in self.query('outsides')]).difference(self.buildings)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def mapped(self):
|
def mapped(self):
|
||||||
|
@ -107,21 +121,21 @@ class LevelGeometries():
|
||||||
@cached_property
|
@cached_property
|
||||||
def lineobstacles(self):
|
def lineobstacles(self):
|
||||||
lineobstacles = []
|
lineobstacles = []
|
||||||
for obstacle in self.level.lineobstacles.all():
|
for obstacle in self.query('lineobstacles'):
|
||||||
lineobstacles.append(obstacle.geometry.buffer(obstacle.width/2,
|
lineobstacles.append(obstacle.geometry.buffer(obstacle.width/2,
|
||||||
join_style=JOIN_STYLE.mitre, cap_style=CAP_STYLE.flat))
|
join_style=JOIN_STYLE.mitre, cap_style=CAP_STYLE.flat))
|
||||||
return cascaded_union(lineobstacles)
|
return cascaded_union(lineobstacles)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def uncropped_obstacles(self):
|
def uncropped_obstacles(self):
|
||||||
obstacles = [obstacle.geometry for obstacle in self.level.obstacles.filter(crop_to_level__isnull=True)]
|
obstacles = [obstacle.geometry for obstacle in self.query('obstacles').filter(crop_to_level__isnull=True)]
|
||||||
return cascaded_union(obstacles).intersection(self.mapped)
|
return cascaded_union(obstacles).intersection(self.mapped)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def cropped_obstacles(self):
|
def cropped_obstacles(self):
|
||||||
levels_by_name = {}
|
levels_by_name = {}
|
||||||
obstacles_by_crop_to_level = {}
|
obstacles_by_crop_to_level = {}
|
||||||
for obstacle in self.level.obstacles.filter(crop_to_level__isnull=False):
|
for obstacle in self.query('obstacles').filter(crop_to_level__isnull=False):
|
||||||
level_name = obstacle.crop_to_level.name
|
level_name = obstacle.crop_to_level.name
|
||||||
levels_by_name.setdefault(level_name, obstacle.crop_to_level)
|
levels_by_name.setdefault(level_name, obstacle.crop_to_level)
|
||||||
obstacles_by_crop_to_level.setdefault(level_name, []).append(obstacle.geometry)
|
obstacles_by_crop_to_level.setdefault(level_name, []).append(obstacle.geometry)
|
||||||
|
@ -140,11 +154,11 @@ class LevelGeometries():
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def raw_doors(self):
|
def raw_doors(self):
|
||||||
return cascaded_union([door.geometry for door in self.level.doors.all()]).intersection(self.mapped)
|
return cascaded_union([door.geometry for door in self.query('doors').all()]).intersection(self.mapped)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def elevatorlevels(self):
|
def elevatorlevels(self):
|
||||||
return cascaded_union([elevatorlevel.geometry for elevatorlevel in self.level.elevatorlevels.all()])
|
return cascaded_union([elevatorlevel.geometry for elevatorlevel in self.query('elevatorlevels').all()])
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def areas(self):
|
def areas(self):
|
||||||
|
@ -152,7 +166,7 @@ class LevelGeometries():
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def holes(self):
|
def holes(self):
|
||||||
return cascaded_union([holes.geometry for holes in self.level.holes.all()]).intersection(self.areas)
|
return cascaded_union([holes.geometry for holes in self.query('holes').all()]).intersection(self.areas)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def accessible(self):
|
def accessible(self):
|
||||||
|
@ -183,18 +197,18 @@ class LevelGeometries():
|
||||||
return self.raw_doors.difference(self.areas)
|
return self.raw_doors.difference(self.areas)
|
||||||
|
|
||||||
def get_levelconnectors(self, to_level=None):
|
def get_levelconnectors(self, to_level=None):
|
||||||
queryset = self.level.levelconnectors.prefetch_related('levels')
|
queryset = self.query('levelconnectors').prefetch_related('levels')
|
||||||
if to_level is not None:
|
if to_level is not None:
|
||||||
queryset = queryset.filter(levels=to_level)
|
queryset = queryset.filter(levels=to_level)
|
||||||
return cascaded_union([levelconnector.geometry for levelconnector in queryset])
|
return cascaded_union([levelconnector.geometry for levelconnector in queryset])
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def levelconnectors(self):
|
def levelconnectors(self):
|
||||||
return cascaded_union([levelconnector.geometry for levelconnector in self.level.levelconnectors])
|
return cascaded_union([levelconnector.geometry for levelconnector in self.query('levelconnectors')])
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def intermediate_shadows(self):
|
def intermediate_shadows(self):
|
||||||
qs = self.level.levelconnectors.prefetch_related('levels').filter(levels__altitude__lt=self.level.altitude)
|
qs = self.query('levelconnectors').prefetch_related('levels').filter(levels__altitude__lt=self.level.altitude)
|
||||||
connectors = cascaded_union([levelconnector.geometry for levelconnector in qs])
|
connectors = cascaded_union([levelconnector.geometry for levelconnector in qs])
|
||||||
shadows = self.buildings.difference(connectors.buffer(0.4, join_style=JOIN_STYLE.mitre))
|
shadows = self.buildings.difference(connectors.buffer(0.4, join_style=JOIN_STYLE.mitre))
|
||||||
shadows = shadows.buffer(0.3)
|
shadows = shadows.buffer(0.3)
|
||||||
|
@ -205,7 +219,7 @@ class LevelGeometries():
|
||||||
holes = self.holes.buffer(0.1, join_style=JOIN_STYLE.mitre)
|
holes = self.holes.buffer(0.1, join_style=JOIN_STYLE.mitre)
|
||||||
shadows = holes.difference(self.holes.buffer(-0.3, join_style=JOIN_STYLE.mitre))
|
shadows = holes.difference(self.holes.buffer(-0.3, join_style=JOIN_STYLE.mitre))
|
||||||
|
|
||||||
qs = self.level.levelconnectors.prefetch_related('levels').filter(levels__altitude__lt=self.level.altitude)
|
qs = self.query('levelconnectors').prefetch_related('levels').filter(levels__altitude__lt=self.level.altitude)
|
||||||
connectors = cascaded_union([levelconnector.geometry for levelconnector in qs])
|
connectors = cascaded_union([levelconnector.geometry for levelconnector in qs])
|
||||||
|
|
||||||
shadows = shadows.difference(connectors.buffer(1.0, join_style=JOIN_STYLE.mitre))
|
shadows = shadows.difference(connectors.buffer(1.0, join_style=JOIN_STYLE.mitre))
|
||||||
|
@ -213,7 +227,7 @@ class LevelGeometries():
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def stairs(self):
|
def stairs(self):
|
||||||
return cascaded_union([stair.geometry for stair in self.level.stairs.all()]).intersection(self.accessible)
|
return cascaded_union([stair.geometry for stair in self.query('stairs')]).intersection(self.accessible)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def stair_areas(self):
|
def stair_areas(self):
|
||||||
|
|
|
@ -7,6 +7,11 @@ from c3nav.mapdata.models import Source
|
||||||
from c3nav.mapdata.utils.cache import get_packages_cached
|
from c3nav.mapdata.utils.cache import get_packages_cached
|
||||||
|
|
||||||
|
|
||||||
|
def get_public_packages():
|
||||||
|
packages_cached = get_packages_cached()
|
||||||
|
return [packages_cached[name] for name in settings.PUBLIC_PACKAGES]
|
||||||
|
|
||||||
|
|
||||||
def get_unlocked_packages_names(request, packages_cached=None):
|
def get_unlocked_packages_names(request, packages_cached=None):
|
||||||
if packages_cached is None:
|
if packages_cached is None:
|
||||||
packages_cached = get_packages_cached()
|
packages_cached = get_packages_cached()
|
||||||
|
|
|
@ -3,9 +3,11 @@ from c3nav.mapdata.render.renderer import LevelRenderer, get_render_path # noqa
|
||||||
|
|
||||||
|
|
||||||
def render_all_levels(show_accessibles=False):
|
def render_all_levels(show_accessibles=False):
|
||||||
|
|
||||||
renderers = []
|
renderers = []
|
||||||
for level in Level.objects.all():
|
for level in Level.objects.all():
|
||||||
renderers.append(LevelRenderer(level))
|
renderers.append(LevelRenderer(level, only_public=False))
|
||||||
|
renderers.append(LevelRenderer(level, only_public=True))
|
||||||
|
|
||||||
for renderer in renderers:
|
for renderer in renderers:
|
||||||
renderer.render_base(show_accessibles=show_accessibles)
|
renderer.render_base(show_accessibles=show_accessibles)
|
||||||
|
|
|
@ -23,8 +23,18 @@ def get_dimensions():
|
||||||
|
|
||||||
|
|
||||||
class LevelRenderer():
|
class LevelRenderer():
|
||||||
def __init__(self, level):
|
def __init__(self, level, only_public):
|
||||||
self.level = level
|
self.level = level
|
||||||
|
self.only_public = only_public
|
||||||
|
|
||||||
|
self.geometries = self.get_geometries(level)
|
||||||
|
|
||||||
|
def get_geometries(self, level):
|
||||||
|
return level.public_geometries if self.only_public else level.geometries
|
||||||
|
|
||||||
|
def get_filename(self, mode, filetype, level=None):
|
||||||
|
return get_render_path('%s%s-level-%s.%s' % (('public-' if self.only_public else ''), mode,
|
||||||
|
(self.level.name if level is None else level), filetype))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_dimensions():
|
def get_dimensions():
|
||||||
|
@ -103,32 +113,32 @@ class LevelRenderer():
|
||||||
MITRE = JOIN_STYLE.mitre
|
MITRE = JOIN_STYLE.mitre
|
||||||
if not self.level.intermediate:
|
if not self.level.intermediate:
|
||||||
width, height = get_dimensions()
|
width, height = get_dimensions()
|
||||||
holes = self.level.geometries.holes.buffer(0.1, join_style=MITRE)
|
holes = self.geometries.holes.buffer(0.1, join_style=MITRE)
|
||||||
contents.append(self.polygon_svg(box(0, 0, width, height).difference(holes),
|
contents.append(self.polygon_svg(box(0, 0, width, height).difference(holes),
|
||||||
fill_color='#000000'))
|
fill_color='#000000'))
|
||||||
|
|
||||||
contents.append(self.polygon_svg(self.level.geometries.buildings_with_holes,
|
contents.append(self.polygon_svg(self.geometries.buildings_with_holes,
|
||||||
fill_color='#D5D5D5'))
|
fill_color='#D5D5D5'))
|
||||||
|
|
||||||
contents.append(self.polygon_svg(self.level.geometries.outsides_with_holes,
|
contents.append(self.polygon_svg(self.geometries.outsides_with_holes,
|
||||||
fill_color='#DCE6DC'))
|
fill_color='#DCE6DC'))
|
||||||
|
|
||||||
contents.append(self.polygon_svg(self.level.geometries.stair_areas,
|
contents.append(self.polygon_svg(self.geometries.stair_areas,
|
||||||
fill_color='#000000',
|
fill_color='#000000',
|
||||||
fill_opacity=0.03))
|
fill_opacity=0.03))
|
||||||
|
|
||||||
contents.append(self.polygon_svg(self.level.geometries.stairs,
|
contents.append(self.polygon_svg(self.geometries.stairs,
|
||||||
stroke_color='#000000',
|
stroke_color='#000000',
|
||||||
stroke_width=0.06,
|
stroke_width=0.06,
|
||||||
stroke_opacity=0.2))
|
stroke_opacity=0.2))
|
||||||
|
|
||||||
contents.append(self.polygon_svg(self.level.geometries.walls_shadow,
|
contents.append(self.polygon_svg(self.geometries.walls_shadow,
|
||||||
fill_color='#000000',
|
fill_color='#000000',
|
||||||
fill_opacity=0.06))
|
fill_opacity=0.06))
|
||||||
|
|
||||||
if show_accessibles:
|
if show_accessibles:
|
||||||
narrowed_geometry = self.level.geometries.accessible.buffer(-0.6, join_style=MITRE)
|
narrowed_geometry = self.geometries.accessible.buffer(-0.6, join_style=MITRE)
|
||||||
clear_geometry = self.level.geometries.accessible.buffer(-0.3, join_style=JOIN_STYLE.mitre)
|
clear_geometry = self.geometries.accessible.buffer(-0.3, join_style=JOIN_STYLE.mitre)
|
||||||
wide_geometry = narrowed_geometry.buffer(0.31, join_style=MITRE).intersection(clear_geometry)
|
wide_geometry = narrowed_geometry.buffer(0.31, join_style=MITRE).intersection(clear_geometry)
|
||||||
missing_geometry = clear_geometry.difference(wide_geometry.buffer(0.01, join_style=MITRE))
|
missing_geometry = clear_geometry.difference(wide_geometry.buffer(0.01, join_style=MITRE))
|
||||||
|
|
||||||
|
@ -144,35 +154,35 @@ class LevelRenderer():
|
||||||
fill_color='#FF9900',
|
fill_color='#FF9900',
|
||||||
fill_opacity=0.5))
|
fill_opacity=0.5))
|
||||||
|
|
||||||
contents.append(self.polygon_svg(self.level.geometries.elevatorlevels,
|
contents.append(self.polygon_svg(self.geometries.elevatorlevels,
|
||||||
fill_color='#9EF8FB'))
|
fill_color='#9EF8FB'))
|
||||||
|
|
||||||
contents.append(self.polygon_svg(self.level.geometries.doors,
|
contents.append(self.polygon_svg(self.geometries.doors,
|
||||||
fill_color='#FFFFFF',
|
fill_color='#FFFFFF',
|
||||||
stroke_color='#3c3c3c',
|
stroke_color='#3c3c3c',
|
||||||
stroke_width=0.05))
|
stroke_width=0.05))
|
||||||
|
|
||||||
contents.append(self.polygon_svg(self.level.geometries.uncropped_obstacles,
|
contents.append(self.polygon_svg(self.geometries.uncropped_obstacles,
|
||||||
fill_color='#BDBDBD',
|
fill_color='#BDBDBD',
|
||||||
stroke_color='#9E9E9E',
|
stroke_color='#9E9E9E',
|
||||||
stroke_width=0.05))
|
stroke_width=0.05))
|
||||||
|
|
||||||
contents.append(self.polygon_svg(self.level.geometries.cropped_obstacles.buffer(-0.06, join_style=MITRE),
|
contents.append(self.polygon_svg(self.geometries.cropped_obstacles.buffer(-0.06, join_style=MITRE),
|
||||||
fill_color='#BDBDBD',
|
fill_color='#BDBDBD',
|
||||||
stroke_color='#9E9E9E',
|
stroke_color='#9E9E9E',
|
||||||
stroke_width=0.05))
|
stroke_width=0.05))
|
||||||
|
|
||||||
contents.append(self.polygon_svg(self.level.geometries.walls,
|
contents.append(self.polygon_svg(self.geometries.walls,
|
||||||
fill_color='#949494',
|
fill_color='#949494',
|
||||||
stroke_color='#3c3c3c',
|
stroke_color='#3c3c3c',
|
||||||
stroke_width=0.05))
|
stroke_width=0.05))
|
||||||
|
|
||||||
filename = get_render_path('level-%s.base.svg' % self.level.name)
|
filename = self.get_filename('base', 'svg')
|
||||||
with open(filename, 'w') as f:
|
with open(filename, 'w') as f:
|
||||||
f.write(ET.tostring(svg).decode())
|
f.write(ET.tostring(svg).decode())
|
||||||
|
|
||||||
if png:
|
if png:
|
||||||
png_filename = get_render_path('level-%s.base.png' % self.level.name)
|
png_filename = self.get_filename('base', 'png')
|
||||||
subprocess.call(['rsvg-convert', filename, '-o', png_filename])
|
subprocess.call(['rsvg-convert', filename, '-o', png_filename])
|
||||||
|
|
||||||
def render_simple(self, png=True):
|
def render_simple(self, png=True):
|
||||||
|
@ -193,7 +203,7 @@ class LevelRenderer():
|
||||||
fill_color='#000000'))
|
fill_color='#000000'))
|
||||||
|
|
||||||
for level in dark_lower:
|
for level in dark_lower:
|
||||||
self.add_svg_image(svg, 'file://'+get_render_path('level-%s.base.png' % level.name))
|
self.add_svg_image(svg, 'file://'+self.get_filename('base', 'png', level=level))
|
||||||
|
|
||||||
contents = self.add_svg_content(svg)
|
contents = self.add_svg_content(svg)
|
||||||
contents.append(self.polygon_svg(box(0, 0, width, height),
|
contents.append(self.polygon_svg(box(0, 0, width, height),
|
||||||
|
@ -201,20 +211,20 @@ class LevelRenderer():
|
||||||
fill_opacity=0.1))
|
fill_opacity=0.1))
|
||||||
|
|
||||||
for level in lower:
|
for level in lower:
|
||||||
self.add_svg_image(svg, 'file://'+get_render_path('level-%s.base.png' % level.name))
|
self.add_svg_image(svg, 'file://'+self.get_filename('base', 'png', level=level))
|
||||||
|
|
||||||
filename = get_render_path('level-%s.simple.svg' % self.level.name)
|
filename = self.get_filename('simple', 'svg')
|
||||||
with open(filename, 'w') as f:
|
with open(filename, 'w') as f:
|
||||||
f.write(ET.tostring(svg).decode())
|
f.write(ET.tostring(svg).decode())
|
||||||
|
|
||||||
if png:
|
if png:
|
||||||
png_filename = get_render_path('level-%s.simple.png' % self.level.name)
|
png_filename = self.get_filename('simple', 'png')
|
||||||
subprocess.call(['rsvg-convert', filename, '-o', png_filename])
|
subprocess.call(['rsvg-convert', filename, '-o', png_filename])
|
||||||
|
|
||||||
def render_full(self, png=True):
|
def render_full(self, png=True):
|
||||||
svg = self.create_svg()
|
svg = self.create_svg()
|
||||||
|
|
||||||
self.add_svg_image(svg, 'file://' + get_render_path('level-%s.simple.png' % self.level.name))
|
self.add_svg_image(svg, 'file://' + self.get_filename('simple', 'png'))
|
||||||
|
|
||||||
higher = []
|
higher = []
|
||||||
for level in self.level.higher():
|
for level in self.level.higher():
|
||||||
|
@ -224,17 +234,17 @@ class LevelRenderer():
|
||||||
|
|
||||||
contents = self.add_svg_content(svg)
|
contents = self.add_svg_content(svg)
|
||||||
for level in higher:
|
for level in higher:
|
||||||
contents.append(self.polygon_svg(level.geometries.intermediate_shadows,
|
contents.append(self.polygon_svg(self.get_geometries(level).intermediate_shadows,
|
||||||
fill_color='#000000',
|
fill_color='#000000',
|
||||||
fill_opacity=0.07))
|
fill_opacity=0.07))
|
||||||
|
|
||||||
for level in higher:
|
for level in higher:
|
||||||
self.add_svg_image(svg, 'file://'+get_render_path('level-%s.base.png' % level.name))
|
self.add_svg_image(svg, 'file://'+self.get_filename('base', 'png', level=level))
|
||||||
|
|
||||||
filename = get_render_path('level-%s.full.svg' % self.level.name)
|
filename = self.get_filename('full', 'svg')
|
||||||
with open(filename, 'w') as f:
|
with open(filename, 'w') as f:
|
||||||
f.write(ET.tostring(svg).decode())
|
f.write(ET.tostring(svg).decode())
|
||||||
|
|
||||||
if png:
|
if png:
|
||||||
png_filename = get_render_path('level-%s.full.png' % self.level.name)
|
png_filename = self.get_filename('full', 'png')
|
||||||
subprocess.call(['rsvg-convert', filename, '-o', png_filename])
|
subprocess.call(['rsvg-convert', filename, '-o', png_filename])
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue