diff --git a/src/c3nav/mapdata/management/commands/rendermap.py b/src/c3nav/mapdata/management/commands/rendermap.py index 732c2592..5d6ab5ea 100644 --- a/src/c3nav/mapdata/management/commands/rendermap.py +++ b/src/c3nav/mapdata/management/commands/rendermap.py @@ -82,6 +82,8 @@ class Command(BaseCommand): help=_('maximum x coordinate, everthing right of it will be cropped')) parser.add_argument('--maxy', default=None, type=float, help=_('maximum y coordinate, everthing above it will be cropped')) + parser.add_argument('--min-width', default=None, type=float, + help=_('ensure that all objects are at least this thick')) def handle(self, *args, **options): (minx, miny), (maxx, maxy) = Source.max_bounds() @@ -101,7 +103,8 @@ class Command(BaseCommand): for level in options['levels']: renderer = MapRenderer(level.pk, minx, miny, maxx, maxy, access_permissions=options['permissions'], - scale=options['scale'], full_levels=options['full_levels']) + scale=options['scale'], full_levels=options['full_levels'], + min_width=options['min_width']) filename = os.path.join(settings.RENDER_ROOT, 'level_%s.%s' % (level.short_label, options['filetype'])) diff --git a/src/c3nav/mapdata/render/engines/base.py b/src/c3nav/mapdata/render/engines/base.py index 4b64b294..494fff10 100644 --- a/src/c3nav/mapdata/render/engines/base.py +++ b/src/c3nav/mapdata/render/engines/base.py @@ -30,7 +30,7 @@ class RenderEngine(ABC): # draw an svg image. supports pseudo-3D shadow-rendering def __init__(self, width: int, height: int, xoff=0, yoff=0, zoff=0, - scale=1, buffer=0, background='#FFFFFF', center=True): + scale=1, buffer=0, background='#FFFFFF', min_width=None, center=True): self.width = width self.height = height self.minx = xoff @@ -40,6 +40,7 @@ class RenderEngine(ABC): self.orig_buffer = buffer self.buffer = int(math.ceil(buffer*self.scale)) self.background = background + self.min_width = min_width self.maxx = self.minx + width / scale self.maxy = self.miny + height / scale diff --git a/src/c3nav/mapdata/render/engines/openscad.py b/src/c3nav/mapdata/render/engines/openscad.py index b580c155..6ab9f848 100644 --- a/src/c3nav/mapdata/render/engines/openscad.py +++ b/src/c3nav/mapdata/render/engines/openscad.py @@ -248,6 +248,8 @@ class OpenSCADEngine(Base3DEngine): if not obstacle.geom.intersects(self.bbox): continue obstacle = obstacle.geom.buffer(0).buffer(0.01, join_style=JOIN_STYLE.mitre) + if self.min_width: + obstacle = obstacle.union(self._satisfy_min_width(obstacle)).buffer(0) obstacle = obstacle.intersection(geometry_buffered) if not obstacle.is_empty: had_height_obstacles = True @@ -278,6 +280,8 @@ class OpenSCADEngine(Base3DEngine): if not obstacle.geom.intersects(self.bbox): continue obstacle = obstacle.geom.buffer(0).buffer(0.01, join_style=JOIN_STYLE.mitre) + if self.min_width: + obstacle = obstacle.union(self._satisfy_min_width(obstacle)).buffer(0) obstacle = obstacle.intersection(geometry_buffered).intersection(self.bbox) if not obstacle.is_empty: had_obstacles = True @@ -289,6 +293,13 @@ class OpenSCADEngine(Base3DEngine): if had_obstacles: main_building_block.append(obstacles_block) + if self.min_width and geoms.on_top_of_id is None: + main_building_block.append( + self._add_polygon('min width', + self._satisfy_min_width(buildings).intersection(self.bbox).buffer(0), + geoms.lower_bound, geoms.upper_bound) + ) + def _add_polygon(self, name, geometry, minz, maxz): geometry = geometry.buffer(0) polygons = [] @@ -340,5 +351,8 @@ class OpenSCADEngine(Base3DEngine): cmd = OpenScadBlock('translate([%f, %f, %f])' % (point1.x, point1.y, altitude1/1000), children=[cmd]) return cmd + def _satisfy_min_width(self, geometry): + return geometry.buffer(self.min_width/2, join_style=JOIN_STYLE.mitre) + def render(self, filename=None): return self.root.render().encode() diff --git a/src/c3nav/mapdata/render/renderer.py b/src/c3nav/mapdata/render/renderer.py index a77b8ad8..c438f69d 100644 --- a/src/c3nav/mapdata/render/renderer.py +++ b/src/c3nav/mapdata/render/renderer.py @@ -10,7 +10,8 @@ from c3nav.mapdata.render.utils import get_full_levels, get_min_altitude class MapRenderer: - def __init__(self, level, minx, miny, maxx, maxy, scale=1, access_permissions=None, full_levels=False): + def __init__(self, level, minx, miny, maxx, maxy, scale=1, access_permissions=None, full_levels=False, + min_width=None): self.level = level.pk if isinstance(level, Level) else level self.minx = minx self.miny = miny @@ -19,6 +20,7 @@ class MapRenderer: self.scale = scale self.access_permissions = set(access_permissions) if access_permissions else set() self.full_levels = full_levels + self.min_width = min_width/self.scale self.width = int(round((maxx - minx) * scale)) self.height = int(round((maxy - miny) * scale)) @@ -36,7 +38,7 @@ class MapRenderer: level_render_data = LevelRenderData.get(self.level) engine = engine_cls(self.width, self.height, self.minx, self.miny, float(level_render_data.base_altitude), - scale=self.scale, buffer=1, background='#DCDCDC', center=center) + scale=self.scale, buffer=1, background='#DCDCDC', center=center, min_width=self.min_width) if hasattr(engine, 'custom_render'): engine.custom_render(level_render_data, access_permissions, self.full_levels)