diff --git a/src/c3nav/api/serializers.py b/src/c3nav/api/serializers.py index cd9a3065..9f29b89a 100644 --- a/src/c3nav/api/serializers.py +++ b/src/c3nav/api/serializers.py @@ -34,6 +34,14 @@ class SourceSerializer(serializers.ModelSerializer): fields = ('name', 'package', 'bounds') +class FeatureTypeSerializer(serializers.Serializer): + name = serializers.CharField() + title = serializers.CharField() + title_plural = serializers.CharField() + geomtype = serializers.CharField() + color = serializers.CharField() + + class HosterSerializer(serializers.Serializer): name = serializers.CharField() base_url = serializers.CharField() diff --git a/src/c3nav/api/urls.py b/src/c3nav/api/urls.py index 68484981..c5059173 100644 --- a/src/c3nav/api/urls.py +++ b/src/c3nav/api/urls.py @@ -8,6 +8,7 @@ router = DefaultRouter() router.register(r'levels', mapdata_views.LevelViewSet) router.register(r'packages', mapdata_views.PackageViewSet) router.register(r'sources', mapdata_views.SourceViewSet) +router.register(r'featuretypes', mapdata_views.FeatureTypeViewSet, base_name='featuretype') router.register(r'hosters', editor_views.HosterViewSet, base_name='hoster') diff --git a/src/c3nav/api/views/mapdata.py b/src/c3nav/api/views/mapdata.py index 0d870185..891b7438 100644 --- a/src/c3nav/api/views/mapdata.py +++ b/src/c3nav/api/views/mapdata.py @@ -3,13 +3,14 @@ import os from django.conf import settings from django.core.files import File -from django.http import HttpResponse +from django.http import Http404, HttpResponse from rest_framework.decorators import detail_route -from rest_framework.viewsets import ReadOnlyModelViewSet +from rest_framework.response import Response +from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet -from ...mapdata.models import Level, Package, Source +from ...mapdata.models import FEATURE_TYPES, Level, Package, Source from ..permissions import filter_source_queryset -from ..serializers import LevelSerializer, PackageSerializer, SourceSerializer +from ..serializers import FeatureTypeSerializer, LevelSerializer, PackageSerializer, SourceSerializer from .cache import AccessCachedViewSetMixin, CachedViewSetMixin @@ -63,3 +64,18 @@ class SourceViewSet(AccessCachedViewSetMixin, ReadOnlyModelViewSet): for chunk in File(open(image_path, 'rb')).chunks(): response.write(chunk) return response + + +class FeatureTypeViewSet(ViewSet): + """ + Get Feature types + """ + def list(self, request, version=None): + serializer = FeatureTypeSerializer(FEATURE_TYPES.values(), many=True, context={'request': request}) + return Response(serializer.data) + + def retrieve(self, request, pk=None, version=None): + if pk not in FEATURE_TYPES: + raise Http404 + serializer = FeatureTypeSerializer(FEATURE_TYPES[pk], context={'request': request}) + return Response(serializer.data) diff --git a/src/c3nav/editor/static/editor/css/editor.css b/src/c3nav/editor/static/editor/css/editor.css index 2269cf01..8edc94c6 100644 --- a/src/c3nav/editor/static/editor/css/editor.css +++ b/src/c3nav/editor/static/editor/css/editor.css @@ -1,9 +1,9 @@ -#mapeditor { +#map { position:absolute; top:54px; bottom:0; left:0; - right:0; + right:350px; } .leaflet-control-layers-overlays label { margin-bottom:0; @@ -22,30 +22,6 @@ padding:0 6px; } -.leaflet-editbar a, .leaflet-editbar a:hover { - font-size: 13px; - width:auto; - min-width:50px; - text-align:left; - padding:0 6px; - display:none; -} -.leaflet-editbar a.current { - display:block; - font-weight:bold; -} -.leaflet-editbar.usable:hover a { - width:80px; - display:block; -} -.leaflet-editbar:not(.usable) a.current, .leaflet-editbar:not(:hover) a.current, .leaflet-drawbar a { - border-radius:4px; - border:0; -} -.leaflet-editbar:not(.usable) a.current:hover { - background-color:#FFFFFF; - cursor:default; -} .leaflet-levels a { font-size:16px; @@ -55,3 +31,17 @@ font-weight:bold; color:#000000; } + +#mapeditcontrols { + position:absolute; + top:54px; + bottom:0; + padding:8px; + width:350px; + right:0; + overflow:auto; +} +#mapeditcontrols legend .btn { + padding-left:5.5px; + padding-right:5.5px; +} diff --git a/src/c3nav/editor/static/editor/js/editor.js b/src/c3nav/editor/static/editor/js/editor.js index 8b650175..d2b40d58 100644 --- a/src/c3nav/editor/static/editor/js/editor.js +++ b/src/c3nav/editor/static/editor/js/editor.js @@ -1,38 +1,9 @@ editor = { - feature_types: { - bounds: { - type: 'polygon', - color: '#FFFFFF', - }, - building: { - type: 'polygon', - color: '#CCCCCC', - }, - wall: { - type: 'polygon', - color: '#333333', - }, - obstacle: { - type: 'polygon', - color: '#999999', - }, - door: { - type: 'polygon', - color: '#FF00FF', - }, - step: { - type: 'polyline', - color: '#FF0000', - }, - elevator: { - type: 'polygon', - color: '#99CC00', - }, - }, + feature_types: {}, init: function() { // Init Map - editor.map = L.map('mapeditor', { + editor.map = L.map('map', { zoom: 2, maxZoom: 10, minZoom: 1, @@ -43,154 +14,64 @@ editor = { L.control.scale({imperial: false}).addTo(editor.map); + editor.get_feature_types(); editor.get_packages(); - editor.init_features(); + editor.get_sources(); + editor.get_levels(); }, - init_features: function() { - editor._feature_type = null; - L.FeatureLayerControl = L.Control.extend({ - options: { - position: 'topleft' - }, - onAdd: function (map) { - var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-editbar usable'); - $('').appendTo(container).html('').attr({ - href: '#', - title: 'disable editing', - name: 'null' - }).on('click', function() { - editor.set_feature_type(null); - }); - var plural; - for (var feature_type in editor.feature_types) { - $('').appendTo(container).text(feature_type+((feature_type.substr(-1) != 's')?'s':'')).attr({ - href: '#', - title: 'edit '+feature_type+' layer', - name: feature_type - }).on('click', editor._layer_button_click); - } - return container; - } - }); - editor.map.addControl(new L.FeatureLayerControl()); - - // Add drawing new features - editor._drawing = null; - editor._adding = null; - - L.DrawControl = L.Control.extend({ - options: { - position: 'topleft' - }, - onAdd: function (map) { - var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-drawbar'); - $('').appendTo(container).text('start drawing').attr({ - href: '#', - title: 'start drawing', - name: '' - }).on('click', function(e) { - e.preventDefault(); - editor.start_drawing(); - }); - - $('').appendTo(container).text('cancel').attr({ - href: '#', - title: 'cancel drawing', - name: '' - }).on('click', function(e) { - e.preventDefault(); - editor.cancel_drawing(); - }); - return container; - } - }); - editor.map.addControl(new L.DrawControl()); - $('#drawcancel').hide(); - - editor.map.on('editable:drawing:commit', function (e) { - editor._drawing = null; - editor._adding = e.layer; - - e.layer.disableEdit(); - L.popup({ - closeButton: false, - autoClose: false, - }).setContent('').setLatLng(e.layer.getCenter()).openOn(editor.map); - console.log(e.layer.toGeoJSON()); - }).on('editable:drawing:cancel', function (e) { - if (editor._drawing !== null && editor._adding === null) { - e.layer.remove(); + get_feature_types: function() { + $.getJSON('/api/v1/featuretypes/', function(feature_types) { + var feature_type; + var editcontrols = $('#mapeditcontrols'); + for(var i=0;i').attr('name', feature_type.name).append( + $('').text(feature_type.title_plural).append( + $('') + ) + ) + ); } }); }, - _layer_button_click: function(e) { - e.preventDefault(); - editor.set_feature_type($(this).attr('name')); - }, - - set_feature_type: function(feature_type) { - if (editor._drawing !== null || editor._adding !== null) return; - - $('.leaflet-editbar .current').removeClass('current'); - $('.leaflet-editbar [name='+feature_type+']').addClass('current'); - editor._feature_type = feature_type; - - $('.leaflet-drawbar').toggle(feature_type !== null); - $('#drawstart').text('add '+feature_type); - }, - start_drawing: function() { - if (editor._feature_type === null) return; - - editor._drawing = editor._feature_type; - var options = editor.feature_types[editor._drawing]; - if (options.type == 'polygon') { - editor.map.editTools.startPolygon(null, options); - } else if (options.type == 'polyline') { - editor.map.editTools.startPolyline(null, options); - } - $('.leaflet-editbar').toggleClass('usable', false); - $('#drawstart').hide(); - $('#drawcancel').show(); - }, - cancel_drawing: function() { - if (editor._drawing === null || editor._adding !== null) return; - editor.map.editTools.stopDrawing(); - editor._drawing = null; - $('.leaflet-editbar').toggleClass('usable', true); - $('#drawcancel').hide(); - $('#drawstart').show(); - }, - + packages: {}, get_packages: function() { $.getJSON('/api/v1/packages/', function(packages) { var bounds = [[0, 0], [0, 0]]; var pkg; for(var i=0;i').appendTo(container).text('cancel').attr({ + href: '#', + title: 'cancel drawing', + name: '' + }).on('click', function(e) { + e.preventDefault(); + editor.cancel_drawing(); + }); + return container; + } + }); + editor.map.addControl(new L.DrawControl()); + + $('#mapeditcontrols').on('click', 'fieldset legend .btn', function() { + console.log($(this).closest('fieldset')); + editor.start_drawing($(this).closest('fieldset').attr('name')); + }); + + editor.map.on('editable:drawing:commit', function (e) { + editor._drawing = null; + editor._adding = e.layer; + + e.layer.disableEdit(); + L.popup({ + closeButton: false, + autoClose: false, + }).setContent('').setLatLng(e.layer.getCenter()).openOn(editor.map); + console.log(e.layer.toGeoJSON()); + }).on('editable:drawing:cancel', function (e) { + if (editor._drawing !== null && editor._adding === null) { + e.layer.remove(); + } + }); + }, + + start_drawing: function(feature_type) { + console.log(feature_type); + editor._drawing = feature_type; + var options = editor.feature_types[feature_type]; + if (options.geomtype == 'polygon') { + editor.map.editTools.startPolygon(null, options); + } else if (options.geomtype == 'polyline') { + editor.map.editTools.startPolyline(null, options); + } + $('.leaflet-editbar').toggleClass('usable', false); + $('#drawstart').hide(); + $('#drawcancel').show(); + }, + cancel_drawing: function() { + if (editor._drawing === null || editor._adding !== null) return; + editor.map.editTools.stopDrawing(); + editor._drawing = null; + $('#drawcancel').hide(); + $('#drawstart').show(); + }, }; -if ($('#mapeditor').length) { +if ($('#mapeditcontrols').length) { editor.init(); } diff --git a/src/c3nav/editor/templates/editor/map.html b/src/c3nav/editor/templates/editor/map.html index 9ade1a64..f5d7f25f 100644 --- a/src/c3nav/editor/templates/editor/map.html +++ b/src/c3nav/editor/templates/editor/map.html @@ -1,5 +1,6 @@ {% extends 'editor/base.html' %} {% load static %} {% block content %} -
+
+
{% endblock %} diff --git a/src/c3nav/mapdata/models/__init__.py b/src/c3nav/mapdata/models/__init__.py index cefc7c5c..ebbac21f 100644 --- a/src/c3nav/mapdata/models/__init__.py +++ b/src/c3nav/mapdata/models/__init__.py @@ -1,4 +1,4 @@ -from .feature import Feature # noqa +from .feature import Feature, FEATURE_TYPES # noqa from .level import Level # noqa from .package import Package # noqa from .source import Source # noqa diff --git a/src/c3nav/mapdata/models/feature.py b/src/c3nav/mapdata/models/feature.py index a582464e..da65b07d 100644 --- a/src/c3nav/mapdata/models/feature.py +++ b/src/c3nav/mapdata/models/feature.py @@ -1,16 +1,28 @@ +from collections import OrderedDict, namedtuple + from django.db import models from django.utils.translation import ugettext_lazy as _ +class FeatureType(namedtuple('FeatureType', ('name', 'title', 'title_plural', 'geomtype', 'color'))): + def __init__(self, *args, **kwartgs): + FEATURE_TYPES[self.name] = self + +FEATURE_TYPES = OrderedDict() +FeatureType('building', _('Building'), _('Buildings'), 'polygon', '#333333') +FeatureType('room', _('Room'), _('Rooms'), 'polygon', '#CCCCCC') +FeatureType('outside', _('Outside Area'), _('Outside Areas'), 'polygon', '#EEEEEE') +FeatureType('obstacle', _('Obstacle'), _('Obstacles'), 'polygon', '#999999') +# FeatureType('door', _('Door'), 'polygon', '#FF00FF') +# FeatureType('step', _('Step'), 'polyline', '#FF0000') +# FeatureType('elevator', _('Elevator'), 'polygon', '#99CC00') + + class Feature(models.Model): """ A map feature """ - TYPES = ( - ('building', _('Building')), - ('room', _('Room')), - ('obstacle', _('Obstacle')), - ) + TYPES = tuple((name, t.title) for name, t in FEATURE_TYPES.items()) name = models.SlugField(_('feature identifier'), primary_key=True, max_length=50, help_text=_('e.g. noc')) package = models.ForeignKey('Package', on_delete=models.CASCADE, related_name='features',