From dd215e3f3db798edf9c2febfb07e08f144fd09be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Sun, 16 Oct 2016 13:20:34 +0200 Subject: [PATCH] add Obstacle and Door Features --- src/c3nav/editor/forms.py | 4 +- src/c3nav/mapdata/api/features.py | 25 +++++++++- .../mapdata/migrations/0002_door_obstacle.py | 48 +++++++++++++++++++ src/c3nav/mapdata/models/features.py | 47 ++++++++++++++++++ src/c3nav/mapdata/serializers/features.py | 24 +++++++--- 5 files changed, 138 insertions(+), 10 deletions(-) create mode 100644 src/c3nav/mapdata/migrations/0002_door_obstacle.py diff --git a/src/c3nav/editor/forms.py b/src/c3nav/editor/forms.py index f1bd9bce..319e6f66 100644 --- a/src/c3nav/editor/forms.py +++ b/src/c3nav/editor/forms.py @@ -8,7 +8,7 @@ from django.forms.widgets import HiddenInput from shapely.geometry.geo import mapping from c3nav.mapdata.models import Package -from c3nav.mapdata.models.features import Inside, Room +from c3nav.mapdata.models.features import Door, Inside, Obstacle, Room from c3nav.mapdata.permissions import get_unlocked_packages @@ -82,3 +82,5 @@ def create_editor_form(feature_model, add_fields=None): def create_editor_forms(): create_editor_form(Inside) create_editor_form(Room) + create_editor_form(Obstacle, ['height']) + create_editor_form(Door) diff --git a/src/c3nav/mapdata/api/features.py b/src/c3nav/mapdata/api/features.py index caffdbc7..f9539ee0 100644 --- a/src/c3nav/mapdata/api/features.py +++ b/src/c3nav/mapdata/api/features.py @@ -5,8 +5,9 @@ from rest_framework.response import Response from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet from c3nav.mapdata.models import FEATURE_TYPES -from c3nav.mapdata.models.features import Inside, Room -from c3nav.mapdata.serializers.features import FeatureTypeSerializer, InsideSerializer, RoomSerializer +from c3nav.mapdata.models.features import Door, Inside, Obstacle, Room +from c3nav.mapdata.serializers.features import (DoorSerializer, FeatureTypeSerializer, InsideSerializer, + ObstacleSerializer, RoomSerializer) class FeatureTypeViewSet(ViewSet): @@ -58,3 +59,23 @@ class RoomViewSet(ReadOnlyModelViewSet): serializer_class = RoomSerializer lookup_field = 'name' lookup_value_regex = '[^/]+' + + +class ObstacleViewSet(ReadOnlyModelViewSet): + """ + List and retrieve Obstcales + """ + queryset = Obstacle.objects.all() + serializer_class = ObstacleSerializer + lookup_field = 'name' + lookup_value_regex = '[^/]+' + + +class DoorViewSet(ReadOnlyModelViewSet): + """ + List and retrieve Doors + """ + queryset = Door.objects.all() + serializer_class = DoorSerializer + lookup_field = 'name' + lookup_value_regex = '[^/]+' diff --git a/src/c3nav/mapdata/migrations/0002_door_obstacle.py b/src/c3nav/mapdata/migrations/0002_door_obstacle.py new file mode 100644 index 00000000..ad7ddf9d --- /dev/null +++ b/src/c3nav/mapdata/migrations/0002_door_obstacle.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.2 on 2016-10-16 11:13 +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', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Door', + 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()), + ('level', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='doors', to='mapdata.Level', verbose_name='level')), + ('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='doors', to='mapdata.Package', verbose_name='map package')), + ], + options={ + 'verbose_name_plural': 'Doors', + 'verbose_name': 'Door', + 'default_related_name': 'doors', + }, + ), + migrations.CreateModel( + name='Obstacle', + 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()), + ('height', models.DecimalField(decimal_places=2, max_digits=4, null=True, verbose_name='height of the obstacle')), + ('level', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='obstacles', to='mapdata.Level', verbose_name='level')), + ('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='obstacles', to='mapdata.Package', verbose_name='map package')), + ], + options={ + 'verbose_name_plural': 'Obstacles', + 'verbose_name': 'Obstacle', + 'default_related_name': 'obstacles', + }, + ), + ] diff --git a/src/c3nav/mapdata/models/features.py b/src/c3nav/mapdata/models/features.py index 43f3a4cd..c57d5dd6 100644 --- a/src/c3nav/mapdata/models/features.py +++ b/src/c3nav/mapdata/models/features.py @@ -82,3 +82,50 @@ class Room(Feature): verbose_name = _('Room') verbose_name_plural = _('Rooms') default_related_name = 'rooms' + + +@register_featuretype +class Obstacle(Feature): + """ + An obstacle + """ + height = models.DecimalField(_('height of the obstacle'), null=True, max_digits=4, decimal_places=2) + + geomtype = 'polygon' + color = '#999999' + + class Meta: + verbose_name = _('Obstacle') + verbose_name_plural = _('Obstacles') + default_related_name = 'obstacles' + + @classmethod + def fromfile(cls, data, file_path): + kwargs = super().fromfile(data, file_path) + + if 'height' in data: + if not isinstance(data['height'], (int, float)): + raise ValueError('altitude has to be int or float.') + kwargs['height'] = data['height'] + + return kwargs + + def tofile(self): + result = super().tofile() + if self.height is not None: + result['height'] = float(self.level.name) + return result + + +@register_featuretype +class Door(Feature): + """ + A connection between two rooms + """ + geomtype = 'polygon' + color = '#FF00FF' + + class Meta: + verbose_name = _('Door') + verbose_name_plural = _('Doors') + default_related_name = 'doors' diff --git a/src/c3nav/mapdata/serializers/features.py b/src/c3nav/mapdata/serializers/features.py index 161b41fa..c7ee031b 100644 --- a/src/c3nav/mapdata/serializers/features.py +++ b/src/c3nav/mapdata/serializers/features.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from c3nav.mapdata.models.features import Inside, Room +from c3nav.mapdata.models.features import Door, Inside, Obstacle, Room from c3nav.mapdata.serializers.fields import GeometryField @@ -25,21 +25,31 @@ class FeatureTypeSerializer(serializers.Serializer): return obj._meta.default_related_name -class InsideSerializer(serializers.ModelSerializer): +class FeatureSerializer(serializers.ModelSerializer): level = serializers.SlugRelatedField(slug_field='name', read_only=True) package = serializers.SlugRelatedField(slug_field='name', read_only=True) geometry = GeometryField() + +class InsideSerializer(FeatureSerializer): class Meta: model = Inside fields = ('name', 'level', 'package', 'geometry') -class RoomSerializer(serializers.ModelSerializer): - level = serializers.SlugRelatedField(slug_field='name', read_only=True) - package = serializers.SlugRelatedField(slug_field='name', read_only=True) - geometry = GeometryField() - +class RoomSerializer(FeatureSerializer): class Meta: model = Room fields = ('name', 'level', 'package', 'geometry') + + +class ObstacleSerializer(FeatureSerializer): + class Meta: + model = Obstacle + fields = ('name', 'level', 'package', 'geometry') + + +class DoorSerializer(FeatureSerializer): + class Meta: + model = Door + fields = ('name', 'level', 'package', 'geometry')