add stairs

This commit is contained in:
Laura Klünder 2016-12-08 12:36:09 +01:00
parent 3090d52831
commit 56b083f714
7 changed files with 167 additions and 32 deletions

View file

@ -168,6 +168,7 @@ editor = {
_editing_layer: null,
_get_geometries_next_time: false,
_geometries: {},
_geometries_shadows: {},
_creating: false,
_editing: null,
_geometry_types: [],
@ -232,6 +233,7 @@ editor = {
get_geometries: function () {
// reload geometries of current level
editor._geometries = {};
editor._geometries_shadows = {};
if (editor._geometries_layer !== null) {
editor.map.removeLayer(editor._geometries_layer);
}
@ -263,24 +265,41 @@ editor = {
'door': '#FF00FF',
'hole': '#66CC66',
'elevatorlevel': '#9EF8FB',
'levelconnector': '#FFFF00'
'levelconnector': '#FFFF00',
'shadow': '#000000',
'stair': '#FF0000'
},
_line_draw_geometry_style: function(style) {
style.stroke = true;
style.opacity = 0.6;
style.color = style.fillColor;
style.weight = 5;
return style;
},
_get_geometry_style: function (feature) {
// style callback for GeoJSON loader
return editor._get_mapitem_type_style(feature.properties.type);
var style = editor._get_mapitem_type_style(feature.properties.type);
if (feature.geometry.type == 'LineString') {
style = editor._line_draw_geometry_style(style);
}
return style
},
_get_mapitem_type_style: function (mapitem_type) {
// get styles for a specific mapitem
return {
stroke: false,
fillColor: editor._geometry_colors[mapitem_type],
weight: 0,
fillOpacity: 0.6,
smoothFactor: 0
};
},
_register_geojson_feature: function (feature, layer) {
// onEachFeature callback for GeoJSON loader register all needed events
editor._geometries[feature.properties.type+'-'+feature.properties.name] = layer;
if (feature.properties.type == 'shadow') {
editor._geometries_shadows[feature.properties.original_type+'-'+feature.properties.original_name] = layer;
} else {
editor._geometries[feature.properties.type+'-'+feature.properties.name] = layer;
}
layer.on('mouseover', editor._hover_geometry_layer)
.on('mouseout', editor._unhighlight_geometry)
.on('click', editor._click_geometry_layer)
@ -361,6 +380,10 @@ editor = {
var name = form.attr('data-name');
var pk = mapitem_type+'-'+name;
editor._geometries_layer.removeLayer(editor._geometries[pk]);
var shadow = editor._geometries_shadows[pk];
if (shadow) {
editor._geometries_layer.removeLayer(shadow);
}
}
editor._editing = L.geoJSON({
@ -384,6 +407,7 @@ editor = {
if (geomtype == 'polygon') {
editor.map.editTools.startPolygon(null, options);
} else if (geomtype == 'polyline') {
options = editor._line_draw_geometry_style(options);
editor.map.editTools.startPolyline(null, options);
}
editor._creating = true;

View file

@ -10,6 +10,7 @@ from rest_framework.response import Response
from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet
from c3nav.mapdata.models import GEOMETRY_MAPITEM_TYPES, Level, Package, Source
from c3nav.mapdata.models.geometry import LineGeometryMapItemWithLevel
from c3nav.mapdata.permissions import filter_queryset_by_package_access, get_unlocked_packages_names
from c3nav.mapdata.serializers.main import LevelSerializer, PackageSerializer, SourceSerializer
from c3nav.mapdata.utils.cache import (CachedReadOnlyViewSetMixin, cache_mapdata_api_response, get_levels_cached,
@ -92,7 +93,10 @@ class GeometryViewSet(ViewSet):
if hasattr(mapitemtype, field_name):
queryset.prefetch_related(field_name)
results.extend(sum((obj.to_geojson() for obj in queryset), []))
if issubclass(mapitemtype, LineGeometryMapItemWithLevel):
results.extend(obj.to_shadow_geojson() for obj in queryset)
results.extend(obj.to_geojson() for obj in queryset)
return Response(results)

View file

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.4 on 2016-12-08 09:37
from __future__ import unicode_literals
import c3nav.mapdata.fields
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mapdata', '0012_auto_20161204_1544'),
]
operations = [
migrations.CreateModel(
name='Stair',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.SlugField(unique=True, verbose_name='Name')),
('geometry', c3nav.mapdata.fields.GeometryField()),
],
options={
'default_related_name': 'stairs',
'verbose_name_plural': 'Stairs',
'verbose_name': 'Stair',
},
),
migrations.AlterModelOptions(
name='level',
options={'ordering': ['altitude'], 'verbose_name': 'Level', 'verbose_name_plural': 'Levels'},
),
migrations.AddField(
model_name='stair',
name='level',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stairs', to='mapdata.Level', verbose_name='level'),
),
migrations.AddField(
model_name='stair',
name='package',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stairs', to='mapdata.Package', verbose_name='map package'),
),
]

View file

@ -1,8 +1,8 @@
from collections import OrderedDict
from django.conf import settings
from django.db import models
from django.utils.translation import ugettext_lazy as _
from shapely.geometry import CAP_STYLE, JOIN_STYLE
from shapely.geometry.geo import mapping, shape
from c3nav.mapdata.fields import GeometryField
@ -26,7 +26,6 @@ class GeometryMapItem(MapItem, metaclass=GeometryMapItemMeta):
A map feature
"""
geometry = GeometryField()
cached_geojson = {}
geomtype = None
@ -50,12 +49,6 @@ class GeometryMapItem(MapItem, metaclass=GeometryMapItemMeta):
return kwargs
@classmethod
def get_styles(cls):
return {
cls.__name__.lower(): cls.color
}
def get_geojson_properties(self):
return OrderedDict((
('type', self.__class__.__name__.lower()),
@ -64,25 +57,20 @@ class GeometryMapItem(MapItem, metaclass=GeometryMapItemMeta):
))
def to_geojson(self):
if settings.DIRECT_EDITING:
return self._to_geojson()
key = (self.__class__, self.name)
if key not in self.cached_geojson:
self.cached_geojson[key] = self._to_geojson()
return self.cached_geojson[key]
def _to_geojson(self):
return [OrderedDict((
return OrderedDict((
('type', 'Feature'),
('properties', self.get_geojson_properties()),
('geometry', format_geojson(mapping(self.geometry), round=False)),
))]
))
def tofile(self):
result = super().tofile()
result['geometry'] = format_geojson(mapping(self.geometry))
return result
def get_shadow_geojson(self):
return None
class GeometryMapItemWithLevel(GeometryMapItem):
"""
@ -289,3 +277,42 @@ class ElevatorLevel(GeometryMapItemWithLevel):
result['elevator'] = self.elevator.name
result['button'] = self.button
return result
class LineGeometryMapItemWithLevel(GeometryMapItemWithLevel):
geomtype = 'polyline'
class Meta:
abstract = True
def to_geojson(self):
result = super().to_geojson()
original_geometry = result['geometry']
draw = self.geometry.buffer(0.05, join_style=JOIN_STYLE.mitre, cap_style=CAP_STYLE.flat)
result['geometry'] = format_geojson(mapping(draw))
result['original_geometry'] = original_geometry
return result
def to_shadow_geojson(self):
shadow = self.geometry.parallel_offset(0.03, 'left', join_style=JOIN_STYLE.mitre)
shadow = shadow.buffer(0.019, join_style=JOIN_STYLE.mitre, cap_style=CAP_STYLE.flat)
return OrderedDict((
('type', 'Feature'),
('properties', OrderedDict((
('type', 'shadow'),
('original_type', self.__class__.__name__.lower()),
('original_name', self.name),
('level', self.level.name),
))),
('geometry', format_geojson(mapping(shadow), round=False)),
))
class Stair(LineGeometryMapItemWithLevel):
"""
A stair
"""
class Meta:
verbose_name = _('Stair')
verbose_name_plural = _('Stairs')
default_related_name = 'stairs'

View file

@ -5,6 +5,7 @@ from shapely.geometry import JOIN_STYLE
from shapely.ops import cascaded_union
from c3nav.mapdata.models.base import MapItem
from c3nav.mapdata.utils.geometry import assert_multilinestring
class Level(MapItem):
@ -193,3 +194,14 @@ class LevelGeometries():
shadows = shadows.difference(connectors.buffer(1.0, join_style=JOIN_STYLE.mitre))
return shadows
@cached_property
def stairs(self):
return cascaded_union([stair.geometry for stair in self.level.stairs.all()])
@cached_property
def stair_shadows(self):
shadows = []
for stair in assert_multilinestring(self.stairs):
shadows.append(stair.parallel_offset(0.1, 'left', join_style=JOIN_STYLE.mitre))
return cascaded_union(shadows)

View file

@ -32,7 +32,8 @@ class LevelRenderer():
return (width * settings.RENDER_SCALE, height * settings.RENDER_SCALE)
@staticmethod
def polygon_svg(geometry, fill_color=None, fill_opacity=None, stroke_width=0.0, stroke_color=None, filter=None):
def polygon_svg(geometry, fill_color=None, fill_opacity=None,
stroke_width=0.0, stroke_color=None, stroke_opacity=None):
scaled = scale(geometry, xfact=settings.RENDER_SCALE, yfact=settings.RENDER_SCALE, origin=(0, 0))
element = ET.fromstring(scaled.svg(0, fill_color or '#FFFFFF'))
if element.tag != 'g':
@ -40,7 +41,11 @@ class LevelRenderer():
new_element.append(element)
element = new_element
for path in element.findall('path'):
paths = element.findall('polyline')
if len(paths) == 0:
paths = element.findall('path')
for path in paths:
path.attrib.pop('opacity')
path.set('stroke-width', str(stroke_width * settings.RENDER_SCALE))
@ -56,8 +61,8 @@ class LevelRenderer():
elif 'stroke' in path.attrib:
path.attrib.pop('stroke')
if filter is not None:
path.set('filter', filter)
if stroke_opacity is not None:
path.set('stroke-opacity', str(stroke_opacity))
return element
@ -107,6 +112,16 @@ class LevelRenderer():
contents.append(self.polygon_svg(self.level.geometries.outsides_with_holes,
fill_color='#DCE6DC'))
contents.append(self.polygon_svg(self.level.geometries.stair_shadows,
stroke_color='#000000',
stroke_width=0.1,
stroke_opacity=0.1))
contents.append(self.polygon_svg(self.level.geometries.stairs,
stroke_color='#000000',
stroke_width=0.06,
stroke_opacity=0.2))
contents.append(self.polygon_svg(self.level.geometries.walls_shadow,
fill_color='#000000',
fill_opacity=0.06))

View file

@ -1,4 +1,4 @@
from shapely.geometry import Polygon
from shapely.geometry import LineString, Polygon
def clean_geometry(geometry):
@ -26,7 +26,16 @@ def assert_multipolygon(geometry):
:return: a list of Polygons
"""
if isinstance(geometry, Polygon):
polygons = [geometry]
else:
polygons = geometry.geoms
return polygons
return [geometry]
return geometry.geoms
def assert_multilinestring(geometry):
"""
given a Geometry or GeometryCollection, return a list of Geometries
:param geometry: a Geometry or a GeometryCollection
:return: a list of Geometries
"""
if isinstance(geometry, LineString):
return [geometry]
return geometry.geoms