diff --git a/src/c3nav/api/urls.py b/src/c3nav/api/urls.py index 5bc388a4..c2ea39ab 100644 --- a/src/c3nav/api/urls.py +++ b/src/c3nav/api/urls.py @@ -8,12 +8,12 @@ from rest_framework.response import Response from rest_framework.routers import SimpleRouter from c3nav.editor.api import EditorViewSet -from c3nav.mapdata.api import (AreaViewSet, BuildingViewSet, ColumnViewSet, DoorViewSet, HoleViewSet, +from c3nav.mapdata.api import (AreaViewSet, BuildingViewSet, ColumnViewSet, DoorViewSet, HoleViewSet, LevelViewSet, LineObstacleViewSet, LocationGroupViewSet, LocationViewSet, ObstacleViewSet, - PointViewSet, SectionViewSet, SourceViewSet, SpaceViewSet, StairViewSet) + PointViewSet, SourceViewSet, SpaceViewSet, StairViewSet) router = SimpleRouter() -router.register(r'sections', SectionViewSet) +router.register(r'levels', LevelViewSet) router.register(r'buildings', BuildingViewSet) router.register(r'spaces', SpaceViewSet) router.register(r'doors', DoorViewSet) diff --git a/src/c3nav/editor/api.py b/src/c3nav/editor/api.py index a41a9222..fe976f10 100644 --- a/src/c3nav/editor/api.py +++ b/src/c3nav/editor/api.py @@ -7,19 +7,19 @@ from rest_framework.response import Response from rest_framework.viewsets import ViewSet from shapely.ops import cascaded_union -from c3nav.mapdata.models import Section, Space +from c3nav.mapdata.models import Level, Space class EditorViewSet(ViewSet): """ Editor API - /geometries/ returns a list of geojson features, you have to specify ?section= or ?space= + /geometries/ returns a list of geojson features, you have to specify ?level= or ?space= /geometrystyles/ returns styling information for all geometry types """ - def _get_section_geometries(self, section: Section): - buildings = section.buildings.all() + def _get_level_geometries(self, level: Level): + buildings = level.buildings.all() buildings_geom = cascaded_union([building.geometry for building in buildings]) - spaces = {space.id: space for space in section.spaces.all()} + spaces = {space.id: space for space in level.spaces.all()} holes_geom = [] for space in spaces.values(): if space.outside: @@ -38,66 +38,66 @@ class EditorViewSet(ViewSet): results = [] results.extend(buildings) - for door in section.doors.all(): + for door in level.doors.all(): results.append(door) results.extend(spaces.values()) return results - def _get_sections_pk(self, section): - sections_under = () - sections_on_top = () - lower_section = section.lower().first() - primary_sections = (section,) + ((lower_section,) if lower_section else ()) - secondary_sections = Section.objects.filter(on_top_of__in=primary_sections).values_list('pk', 'on_top_of') - if lower_section: - sections_under = tuple(pk for pk, on_top_of in secondary_sections if on_top_of == lower_section.pk) + def _get_levels_pk(self, level): + levels_under = () + levels_on_top = () + lower_level = level.lower().first() + primary_levels = (level,) + ((lower_level,) if lower_level else ()) + secondary_levels = Level.objects.filter(on_top_of__in=primary_levels).values_list('pk', 'on_top_of') + if lower_level: + levels_under = tuple(pk for pk, on_top_of in secondary_levels if on_top_of == lower_level.pk) if True: - sections_on_top = tuple(pk for pk, on_top_of in secondary_sections if on_top_of == section.pk) - sections = chain([section.pk], sections_under, sections_on_top) - return sections, sections_on_top, sections_under + levels_on_top = tuple(pk for pk, on_top_of in secondary_levels if on_top_of == level.pk) + levels = chain([level.pk], levels_under, levels_on_top) + return levels, levels_on_top, levels_under @list_route(methods=['get']) def geometries(self, request, *args, **kwargs): - section = request.GET.get('section') + level = request.GET.get('level') space = request.GET.get('space') - if section is not None: + if level is not None: if space is not None: - raise ValidationError('Only section or space can be specified.') - section = get_object_or_404(Section, pk=section) + raise ValidationError('Only level or space can be specified.') + level = get_object_or_404(Level, pk=level) - sections, sections_on_top, sections_under = self._get_sections_pk(section) - sections = Section.objects.filter(pk__in=sections).prefetch_related('buildings', 'spaces', 'doors', - 'spaces__groups', 'spaces__holes', - 'spaces__columns') - sections = {s.pk: s for s in sections} + levels, levels_on_top, levels_under = self._get_levels_pk(level) + levels = Level.objects.filter(pk__in=levels).prefetch_related('buildings', 'spaces', 'doors', + 'spaces__groups', 'spaces__holes', + 'spaces__columns') + levels = {s.pk: s for s in levels} - section = sections[section.pk] - sections_under = [sections[pk] for pk in sections_under] - sections_on_top = [sections[pk] for pk in sections_on_top] + level = levels[level.pk] + levels_under = [levels[pk] for pk in levels_under] + levels_on_top = [levels[pk] for pk in levels_on_top] results = chain( - *(self._get_section_geometries(s) for s in sections_under), - self._get_section_geometries(section), - *(self._get_section_geometries(s) for s in sections_on_top) + *(self._get_level_geometries(s) for s in levels_under), + self._get_level_geometries(level), + *(self._get_level_geometries(s) for s in levels_on_top) ) return Response([obj.to_geojson() for obj in results]) elif space is not None: - space = get_object_or_404(Space.objects.select_related('section', 'section__on_top_of'), pk=space) - section = space.section + space = get_object_or_404(Space.objects.select_related('level', 'level__on_top_of'), pk=space) + level = space.level - doors = [door for door in section.doors.all() if door.geometry.intersects(space.geometry)] + doors = [door for door in level.doors.all() if door.geometry.intersects(space.geometry)] doors_space_geom = cascaded_union([door.geometry for door in doors]+[space.geometry]) - sections, sections_on_top, sections_under = self._get_sections_pk(section.primary_section) - other_spaces = Space.objects.filter(section__pk__in=sections).prefetch_related('groups') + levels, levels_on_top, levels_under = self._get_levels_pk(level.primary_level) + other_spaces = Space.objects.filter(level__pk__in=levels).prefetch_related('groups') other_spaces = [s for s in other_spaces if s.geometry.intersects(doors_space_geom) and s.pk != space.pk] space.bounds = True - buildings = section.buildings.all() + buildings = level.buildings.all() buildings_geom = cascaded_union([building.geometry for building in buildings]) for other_space in other_spaces: if other_space.outside: @@ -122,7 +122,7 @@ class EditorViewSet(ViewSet): ) return Response(sum([self._get_geojsons(obj) for obj in results], ())) else: - raise ValidationError('No section or space specified.') + raise ValidationError('No level or space specified.') def _get_geojsons(self, obj): return ((obj.to_shadow_geojson(),) if hasattr(obj, 'to_shadow_geojson') else ()) + (obj.to_geojson(),) diff --git a/src/c3nav/editor/forms.py b/src/c3nav/editor/forms.py index 8fd63676..536f78ff 100644 --- a/src/c3nav/editor/forms.py +++ b/src/c3nav/editor/forms.py @@ -16,9 +16,9 @@ class MapitemFormMixin(ModelForm): super().__init__(*args, **kwargs) creating = not self.instance.pk - if 'section' in self.fields: - # hide section widget - self.fields['section'].widget = HiddenInput() + if 'level' in self.fields: + # hide level widget + self.fields['level'].widget = HiddenInput() if 'space' in self.fields: # hide space widget diff --git a/src/c3nav/editor/static/editor/css/editor.css b/src/c3nav/editor/static/editor/css/editor.css index bfef1d39..cb3cc9e6 100644 --- a/src/c3nav/editor/static/editor/css/editor.css +++ b/src/c3nav/editor/static/editor/css/editor.css @@ -110,7 +110,7 @@ legend { right: 8px; top: 8px; } -[data-sections], [data-subsections] { +[data-levels], [data-sublevels] { display:none; } form button.invisiblesubmit { @@ -211,47 +211,47 @@ form button.invisiblesubmit { } -/* leaftlet sections control */ -.leaflet-control-sections { +/* leaftlet levels control */ +.leaflet-control-levels { overflow:hidden; } -.leaflet-control-subsections { +.leaflet-control-sublevels { position:absolute; } -.leaflet-control-sections a, .leaflet-control-sections a:hover { +.leaflet-control-levels a, .leaflet-control-levels a:hover { width: auto; font-size: 14px; padding: 0 7px; white-space: nowrap; } -.leaflet-touch .leaflet-control-sections a, .leaflet-touch .leaflet-control-sections a:hover { +.leaflet-touch .leaflet-control-levels a, .leaflet-touch .leaflet-control-levels a:hover { width: auto; height:36px; line-height:37px; font-size: 14px; padding: 0 12px; } -.leaflet-control-sections a, .leaflet-control-sections a:hover { +.leaflet-control-levels a, .leaflet-control-levels a:hover { opacity:0; margin-top:-26px; } -.leaflet-touch .leaflet-control-sections a, .leaflet-touch .leaflet-control-sections a:hover { +.leaflet-touch .leaflet-control-levels a, .leaflet-touch .leaflet-control-levels a:hover { margin-top:-36px; } -.leaflet-control-sections a.current, .leaflet-control-sections-expanded a, .leaflet-control-sections-expanded a:hover, -.leaflet-touch .leaflet-control-sections a.current, .leaflet-touch .leaflet-control-sections-expanded a, -.leaflet-touch .leaflet-control-sections-expanded a:hover { +.leaflet-control-levels a.current, .leaflet-control-levels-expanded a, .leaflet-control-levels-expanded a:hover, +.leaflet-touch .leaflet-control-levels a.current, .leaflet-touch .leaflet-control-levels-expanded a, +.leaflet-touch .leaflet-control-levels-expanded a:hover { opacity:1; margin-top:0; } -.leaflet-control-sections:not(.leaflet-control-sections-expanded) a.current { +.leaflet-control-levels:not(.leaflet-control-levels-expanded) a.current { border-radius:4px; border-width:0; } -.leaflet-control-sections a.current { +.leaflet-control-levels a.current { font-weight:bold; } -.leaflet-control-sections .leaflet-disabled.current { +.leaflet-control-levels .leaflet-disabled.current { color:#000000; background-color:#ffffff; } diff --git a/src/c3nav/editor/static/editor/js/editor.js b/src/c3nav/editor/static/editor/js/editor.js index b7d78558..aeaac821 100644 --- a/src/c3nav/editor/static/editor/js/editor.js +++ b/src/c3nav/editor/static/editor/js/editor.js @@ -38,11 +38,11 @@ editor = { $('body').removeClass('show-map'); }); - editor._section_control = new SectionControl().addTo(editor.map); - editor._subsection_control = new SectionControl({addClasses: 'leaflet-control-subsections'}).addTo(editor.map); + editor._level_control = new LevelControl().addTo(editor.map); + editor._sublevel_control = new LevelControl({addClasses: 'leaflet-control-sublevels'}).addTo(editor.map); - editor._section_control_container = $(editor._section_control._container); - editor._subsection_control_container = $(editor._subsection_control._container); + editor._level_control_container = $(editor._level_control._container); + editor._sublevel_control_container = $(editor._sublevel_control._container); editor.init_geometries(); }, @@ -105,30 +105,30 @@ editor = { }, _sidebar_unload: function() { // unload the sidebar. called on sidebar_get and form submit. - editor._section_control.disable(); - editor._subsection_control.disable(); + editor._level_control.disable(); + editor._sublevel_control.disable(); $('#sidebar').addClass('loading').find('.content').html(''); editor._cancel_editing(); }, - _fill_section_control: function (section_control, section_list) { - var sections = section_list.find('a'); - if (sections.length) { + _fill_level_control: function (level_control, level_list) { + var levels = level_list.find('a'); + if (levels.length) { var current; - for (var i = 0; i < sections.length; i++) { - var section = $(sections[i]); - section_control.addSection(section.attr('data-id'), section.text(), section.attr('href'), section.is('.current')); + for (var i = 0; i < levels.length; i++) { + var level = $(levels[i]); + level_control.addLevel(level.attr('data-id'), level.text(), level.attr('href'), level.is('.current')); } - if (sections.length > 1) { - section_control.enable(); + if (levels.length > 1) { + level_control.enable(); } else { - section_control.disable(); + level_control.disable(); } - section_control.show() + level_control.show() } else { - section_control.hide(); + level_control.hide(); } - section_control.current_id = parseInt(section_list.attr('data-current-id')); + level_control.current_id = parseInt(level_list.attr('data-current-id')); }, _sidebar_loaded: function(data) { // sidebar was loaded. load the content. check if there are any redirects. call _check_start_editing. @@ -157,28 +157,28 @@ editor = { (editing_id.length ? editing_id.attr('data-editing') : null) ); $('body').addClass('map-enabled'); - editor._section_control.clearSections(); - editor._subsection_control.clearSections(); + editor._level_control.clearLevels(); + editor._sublevel_control.clearLevels(); - editor._fill_section_control(editor._section_control, content.find('[data-sections]')); - editor._fill_section_control(editor._subsection_control, content.find('[data-subsections]')); + editor._fill_level_control(editor._level_control, content.find('[data-levels]')); + editor._fill_level_control(editor._sublevel_control, content.find('[data-sublevels]')); - var section_control_offset = $(editor._section_control_container).position(); - var offset_parent = $(editor._section_control_container).offsetParent(); - $(editor._subsection_control._container).css({ - bottom: offset_parent.height()-section_control_offset.top-editor._section_control_container.height()-parseInt(editor._section_control_container.css('margin-bottom')), - right: offset_parent.width()-section_control_offset.left + var level_control_offset = $(editor._level_control_container).position(); + var offset_parent = $(editor._level_control_container).offsetParent(); + $(editor._sublevel_control._container).css({ + bottom: offset_parent.height()-level_control_offset.top-editor._level_control_container.height()-parseInt(editor._level_control_container.css('margin-bottom')), + right: offset_parent.width()-level_control_offset.left }); } else { $('body').removeClass('map-enabled').removeClass('show-map'); - editor._section_control.hide(); - editor._subsection_control.hide(); + editor._level_control.hide(); + editor._sublevel_control.hide(); } }, _sidebar_error: function(data) { $('#sidebar').removeClass('loading').find('.content').html('

Error '+data.status+'

'+data.statusText); - editor._section_control.hide(); - editor._subsection_control.hide(); + editor._level_control.hide(); + editor._sublevel_control.hide(); }, _sidebar_link_click: function(e) { // listener for link-clicks in the sidebar. @@ -314,14 +314,14 @@ editor = { _get_geometry_style: function (feature) { // style callback for GeoJSON loader var style = editor._get_mapitem_type_style(feature.properties.type); - if (editor._section_control.current_section_id === editor._subsection_control.current_section_id) { - if (editor._subsection_control.section_ids.indexOf(feature.properties.section) >= 0 && editor._section_control.current_section_id !== feature.properties.section) { + if (editor._level_control.current_level_id === editor._sublevel_control.current_level_id) { + if (editor._sublevel_control.level_ids.indexOf(feature.properties.level) >= 0 && editor._level_control.current_level_id !== feature.properties.level) { style.stroke = true; style.weight = 1; style.color = '#ffffff'; } } else { - if (editor._subsection_control.current_section_id !== feature.properties.section) { + if (editor._sublevel_control.current_level_id !== feature.properties.level) { style.fillOpacity = 0.5; } } @@ -529,18 +529,18 @@ editor = { }; -SectionControl = L.Control.extend({ +LevelControl = L.Control.extend({ options: { position: 'bottomright', addClasses: '' }, onAdd: function () { - this._container = L.DomUtil.create('div', 'leaflet-control-sections leaflet-bar '+this.options.addClasses); - this._sectionButtons = []; + this._container = L.DomUtil.create('div', 'leaflet-control-levels leaflet-bar '+this.options.addClasses); + this._levelButtons = []; //noinspection JSUnusedGlobalSymbols - this.current_section_id = null; - this.section_ids = []; + this.current_level_id = null; + this.level_ids = []; this._disabled = true; this._expanded = false; this.hide(); @@ -561,9 +561,9 @@ SectionControl = L.Control.extend({ return this._container; }, - addSection: function (id, title, href, current) { - this.section_ids.push(parseInt(id)); - if (current) this.current_section_id = parseInt(id); + addLevel: function (id, title, href, current) { + this.level_ids.push(parseInt(id)); + if (current) this.current_level_id = parseInt(id); var link = L.DomUtil.create('a', (current ? 'current' : ''), this._container); link.innerHTML = title; @@ -571,32 +571,32 @@ SectionControl = L.Control.extend({ L.DomEvent .on(link, 'mousedown dblclick', L.DomEvent.stopPropagation) - .on(link, 'click', this._sectionClick, this); + .on(link, 'click', this._levelClick, this); - this._sectionButtons.push(link); + this._levelButtons.push(link); return link; }, - clearSections: function() { - this.current_section_id = null; - this.section_ids = []; - for (var i = 0; i < this._sectionButtons.length; i++) { - L.DomUtil.remove(this._sectionButtons[i]); + clearLevels: function() { + this.current_level_id = null; + this.level_ids = []; + for (var i = 0; i < this._levelButtons.length; i++) { + L.DomUtil.remove(this._levelButtons[i]); } - this._sectionButtons = []; + this._levelButtons = []; }, disable: function () { - for (var i = 0; i < this._sectionButtons.length; i++) { - L.DomUtil.addClass(this._sectionButtons[i], 'leaflet-disabled'); + for (var i = 0; i < this._levelButtons.length; i++) { + L.DomUtil.addClass(this._levelButtons[i], 'leaflet-disabled'); } this.collapse(); this._disabled = true; }, enable: function () { - for (var i = 0; i < this._sectionButtons.length; i++) { - L.DomUtil.removeClass(this._sectionButtons[i], 'leaflet-disabled'); + for (var i = 0; i < this._levelButtons.length; i++) { + L.DomUtil.removeClass(this._levelButtons[i], 'leaflet-disabled'); } this._disabled = false; }, @@ -609,7 +609,7 @@ SectionControl = L.Control.extend({ this._container.style.display = ''; }, - _sectionClick: function (e) { + _levelClick: function (e) { e.preventDefault(); e.stopPropagation(); if (!this._expanded) { @@ -625,13 +625,13 @@ SectionControl = L.Control.extend({ expand: function () { if (this._disabled) return; this._expanded = true; - L.DomUtil.addClass(this._container, 'leaflet-control-sections-expanded'); + L.DomUtil.addClass(this._container, 'leaflet-control-levels-expanded'); return this; }, collapse: function () { this._expanded = false; - L.DomUtil.removeClass(this._container, 'leaflet-control-sections-expanded'); + L.DomUtil.removeClass(this._container, 'leaflet-control-levels-expanded'); return this; } }); diff --git a/src/c3nav/editor/templates/editor/delete.html b/src/c3nav/editor/templates/editor/delete.html index 4036ba53..fba21a58 100644 --- a/src/c3nav/editor/templates/editor/delete.html +++ b/src/c3nav/editor/templates/editor/delete.html @@ -1,6 +1,6 @@ {% load bootstrap3 %} {% load i18n %} -{% include 'editor/fragment_sections.html' %} +{% include 'editor/fragment_levels.html' %}

{% blocktrans %}Delete {{ model_title }}{% endblocktrans %}

diff --git a/src/c3nav/editor/templates/editor/edit.html b/src/c3nav/editor/templates/editor/edit.html index dd67bc25..d1d320e7 100644 --- a/src/c3nav/editor/templates/editor/edit.html +++ b/src/c3nav/editor/templates/editor/edit.html @@ -1,7 +1,7 @@ {% load bootstrap3 %} {% load i18n %} -{% include 'editor/fragment_sections.html' %} +{% include 'editor/fragment_levels.html' %}

{% if new %} @@ -10,8 +10,8 @@ {% blocktrans %}Edit {{ model_title }}{% endblocktrans %} {% endif %} {% if on_top_of %} - {% with on_top_of.title as on_top_of_section_title %} - {% blocktrans %}on top of {{ on_top_of_section_title }}{% endblocktrans %} + {% with on_top_of.title as on_top_of_level_title %} + {% blocktrans %}on top of {{ on_top_of_level_title }}{% endblocktrans %} {% endwith %} {% endif %}

diff --git a/src/c3nav/editor/templates/editor/fragment_levels.html b/src/c3nav/editor/templates/editor/fragment_levels.html new file mode 100644 index 00000000..61437586 --- /dev/null +++ b/src/c3nav/editor/templates/editor/fragment_levels.html @@ -0,0 +1,26 @@ +{% if levels %} + + +{% elif level %} + + +{% endif %} +{% if geometry_url %} + +{% endif %} diff --git a/src/c3nav/editor/templates/editor/fragment_sections.html b/src/c3nav/editor/templates/editor/fragment_sections.html deleted file mode 100644 index 1b9fe879..00000000 --- a/src/c3nav/editor/templates/editor/fragment_sections.html +++ /dev/null @@ -1,26 +0,0 @@ -{% if sections %} - - -{% elif section %} - - -{% endif %} -{% if geometry_url %} - -{% endif %} diff --git a/src/c3nav/editor/templates/editor/index.html b/src/c3nav/editor/templates/editor/index.html index c8428bec..693047ba 100644 --- a/src/c3nav/editor/templates/editor/index.html +++ b/src/c3nav/editor/templates/editor/index.html @@ -1,15 +1,15 @@ {% load bootstrap3 %} {% load i18n %} - - {% trans 'Section' as model_title %} + + {% trans 'Level' as model_title %} {% blocktrans %}New {{ model_title }}{% endblocktrans %} -

{% trans 'Sections' %}

+

{% trans 'Level' %}

- {% for s in sections %} - - {{ s.title }} + {% for l in levels %} + + {{ l.title }} {% endfor %}
diff --git a/src/c3nav/editor/templates/editor/level.html b/src/c3nav/editor/templates/editor/level.html new file mode 100644 index 00000000..2627dd73 --- /dev/null +++ b/src/c3nav/editor/templates/editor/level.html @@ -0,0 +1,42 @@ +{% load bootstrap3 %} +{% load i18n %} +{% include 'editor/fragment_levels.html' %} + + + {% trans 'Level' as model_title %} + {% blocktrans %}Edit {{ model_title }}{% endblocktrans %} + +

+

+ {{ level.title }} + {% if level.on_top_of != None %} + {% with level.on_top_of.title as on_top_of_level_title %} + {% blocktrans %}on top of {{ on_top_of_level_title }}{% endblocktrans %} + {% endwith %} + {% endif %} +

+

+ {% if level.on_top_of == None %} + « {% trans 'back to overview' %} + {% else %} + « {% trans 'back to parent level' %} + {% endif %} +

+ +{% include 'editor/fragment_child_models.html' %} + +
+ +{% if level.on_top_of == None %} + + {% blocktrans %}New {{ model_title }}{% endblocktrans %} + +

{% trans 'Levels on top' %}

+
+{% for l in levels_on_top %} + + {{ l.title }} + +{% endfor %} +
+{% endif %} diff --git a/src/c3nav/editor/templates/editor/list.html b/src/c3nav/editor/templates/editor/list.html index 883a6d1c..d85bdda0 100644 --- a/src/c3nav/editor/templates/editor/list.html +++ b/src/c3nav/editor/templates/editor/list.html @@ -1,16 +1,16 @@ {% load bootstrap3 %} {% load i18n %} -{% include 'editor/fragment_sections.html' %} +{% include 'editor/fragment_levels.html' %} {% blocktrans %}New {{ model_title }}{% endblocktrans %}

{% blocktrans %}{{ model_title_plural }}{% endblocktrans %} - {% if section %} - {% with section.title as section_title %} - {% blocktrans %}in section {{ section_title }}{% endblocktrans %} + {% if level %} + {% with level.title as level_title %} + {% blocktrans %}on level {{ level_title }}{% endblocktrans %} {% endwith %} {% endif %} {% if space %} diff --git a/src/c3nav/editor/templates/editor/section.html b/src/c3nav/editor/templates/editor/section.html deleted file mode 100644 index 4fe5cf22..00000000 --- a/src/c3nav/editor/templates/editor/section.html +++ /dev/null @@ -1,41 +0,0 @@ -{% load bootstrap3 %} -{% load i18n %} -{% include 'editor/fragment_sections.html' %} - - - {% trans 'Section' as model_title %} - {% blocktrans %}Edit {{ model_title }}{% endblocktrans %} - -

- {{ section.title }} - {% if section.on_top_of != None %} - {% with section.on_top_of.title as on_top_of_section_title %} - {% blocktrans %}on top of {{ on_top_of_section_title }}{% endblocktrans %} - {% endwith %} - {% endif %} -

-

- {% if section.on_top_of == None %} - « {% trans 'back to overview' %} - {% else %} - « {% trans 'back to parent section' %} - {% endif %} -

- -{% include 'editor/fragment_child_models.html' %} - -
- -{% if section.on_top_of == None %} - - {% blocktrans %}New {{ model_title }}{% endblocktrans %} - -

{% trans 'Sections on top' %}

-
-{% for s in sections_on_top %} - - {{ s.title }} - -{% endfor %} -
-{% endif %} diff --git a/src/c3nav/editor/templates/editor/space.html b/src/c3nav/editor/templates/editor/space.html index 795a23ba..7581e948 100644 --- a/src/c3nav/editor/templates/editor/space.html +++ b/src/c3nav/editor/templates/editor/space.html @@ -1,14 +1,14 @@ {% load bootstrap3 %} {% load i18n %} -{% include 'editor/fragment_sections.html' %} +{% include 'editor/fragment_levels.html' %} - + {% trans 'Space' as model_title %} {% blocktrans %}Edit {{ model_title }}{% endblocktrans %}

{{ space.title }}

- « {% trans 'back to overview' %} + « {% trans 'back to overview' %}

{% include 'editor/fragment_child_models.html' %} diff --git a/src/c3nav/editor/urls.py b/src/c3nav/editor/urls.py index ffc458a1..289733e5 100644 --- a/src/c3nav/editor/urls.py +++ b/src/c3nav/editor/urls.py @@ -1,7 +1,7 @@ from django.apps import apps from django.conf.urls import url -from c3nav.editor.views import edit, list_objects, main_index, section_detail, space_detail +from c3nav.editor.views import edit, level_detail, list_objects, main_index, space_detail def add_editor_urls(model_name, parent_model_name=None, with_list=True, explicit_edit=False): @@ -30,17 +30,17 @@ def add_editor_urls(model_name, parent_model_name=None, with_list=True, explicit urlpatterns = [ url(r'^$', main_index, name='editor.index'), - url(r'^sections/(?P[0-9]+)/$', section_detail, name='editor.sections.detail'), - url(r'^sections/(?P
[0-9]+)/spaces/(?P[0-9]+)/$', space_detail, name='editor.spaces.detail'), - url(r'^sections/(?P[0-9]+)/sections_on_top/create$', edit, name='editor.sections_on_top.create', - kwargs={'model': 'Section'}), + url(r'^levels/(?P[0-9]+)/$', level_detail, name='editor.levels.detail'), + url(r'^levels/(?P[0-9]+)/spaces/(?P[0-9]+)/$', space_detail, name='editor.spaces.detail'), + url(r'^levels/(?P[0-9]+)/levels_on_top/create$', edit, name='editor.levels_on_top.create', + kwargs={'model': 'Level'}), ] -urlpatterns.extend(add_editor_urls('Section', with_list=False, explicit_edit=True)) +urlpatterns.extend(add_editor_urls('Level', with_list=False, explicit_edit=True)) urlpatterns.extend(add_editor_urls('LocationGroup')) urlpatterns.extend(add_editor_urls('Source')) -urlpatterns.extend(add_editor_urls('Building', 'Section')) -urlpatterns.extend(add_editor_urls('Space', 'Section', explicit_edit=True)) -urlpatterns.extend(add_editor_urls('Door', 'Section')) +urlpatterns.extend(add_editor_urls('Building', 'Level')) +urlpatterns.extend(add_editor_urls('Space', 'Level', explicit_edit=True)) +urlpatterns.extend(add_editor_urls('Door', 'Level')) urlpatterns.extend(add_editor_urls('Hole', 'Space')) urlpatterns.extend(add_editor_urls('Area', 'Space')) urlpatterns.extend(add_editor_urls('Stair', 'Space')) diff --git a/src/c3nav/editor/views.py b/src/c3nav/editor/views.py index 832979f7..34d5c247 100644 --- a/src/c3nav/editor/views.py +++ b/src/c3nav/editor/views.py @@ -10,7 +10,7 @@ from django.urls import reverse from django.utils.translation import ugettext_lazy as _ from django.views.decorators.cache import never_cache -from c3nav.mapdata.models import Section, Space +from c3nav.mapdata.models import Level, Space from c3nav.mapdata.models.base import EDITOR_FORM_MODELS @@ -39,7 +39,7 @@ def child_model(model_name, kwargs=None, parent=None): @sidebar_view def main_index(request): return render(request, 'editor/index.html', { - 'sections': Section.objects.filter(on_top_of__isnull=True), + 'levels': Level.objects.filter(on_top_of__isnull=True), 'child_models': [ child_model('LocationGroup'), child_model('Source'), @@ -48,28 +48,28 @@ def main_index(request): @sidebar_view -def section_detail(request, pk): - section = get_object_or_404(Section.objects.select_related('on_top_of'), pk=pk) +def level_detail(request, pk): + level = get_object_or_404(Level.objects.select_related('on_top_of'), pk=pk) - return render(request, 'editor/section.html', { - 'sections': Section.objects.filter(on_top_of__isnull=True), - 'section': section, - 'section_url': 'editor.sections.detail', - 'section_as_pk': True, + return render(request, 'editor/level.html', { + 'levels': Level.objects.filter(on_top_of__isnull=True), + 'level': level, + 'level_url': 'editor.levels.detail', + 'level_as_pk': True, - 'child_models': [child_model(model_name, kwargs={'section': pk}, parent=section) + 'child_models': [child_model(model_name, kwargs={'level': pk}, parent=level) for model_name in ('Building', 'Space', 'Door')], - 'sections_on_top': section.sections_on_top.all(), - 'geometry_url': '/api/editor/geometries/?section='+str(section.primary_section_pk), + 'levels_on_top': level.levels_on_top.all(), + 'geometry_url': '/api/editor/geometries/?level='+str(level.primary_level_pk), }) @sidebar_view -def space_detail(request, section, pk): - space = get_object_or_404(Space, section__id=section, pk=pk) +def space_detail(request, level, pk): + space = get_object_or_404(Space, level__id=level, pk=pk) return render(request, 'editor/space.html', { - 'section': space.section, + 'level': space.level, 'space': space, 'child_models': [child_model(model_name, kwargs={'space': pk}, parent=space) @@ -79,7 +79,7 @@ def space_detail(request, section, pk): @sidebar_view -def edit(request, pk=None, model=None, section=None, space=None, on_top_of=None, explicit_edit=False): +def edit(request, pk=None, model=None, level=None, space=None, on_top_of=None, explicit_edit=False): model = EDITOR_FORM_MODELS[model] related_name = model._meta.default_related_name @@ -87,19 +87,19 @@ def edit(request, pk=None, model=None, section=None, space=None, on_top_of=None, if pk is not None: # Edit existing map item kwargs = {'pk': pk} - if section is not None: - kwargs.update({'section__id': section}) + if level is not None: + kwargs.update({'level__id': level}) elif space is not None: kwargs.update({'space__id': space}) obj = get_object_or_404(model, **kwargs) if False: # todo can access raise PermissionDenied - elif section is not None: - section = get_object_or_404(Section, pk=section) + elif level is not None: + level = get_object_or_404(Level, pk=level) elif space is not None: space = get_object_or_404(Space, pk=space) elif on_top_of is not None: - on_top_of = get_object_or_404(Section.objects.filter(on_top_of__isnull=True), pk=on_top_of) + on_top_of = get_object_or_404(Level.objects.filter(on_top_of__isnull=True), pk=on_top_of) new = obj is None # noinspection PyProtectedMember @@ -117,55 +117,55 @@ def edit(request, pk=None, model=None, section=None, space=None, on_top_of=None, 'geomtype': model._meta.get_field('geometry').geomtype, }) - if model == Section: + if model == Level: ctx.update({ - 'section': obj, - 'back_url': reverse('editor.index') if new else reverse('editor.sections.detail', kwargs={'pk': pk}), + 'level': obj, + 'back_url': reverse('editor.index') if new else reverse('editor.levels.detail', kwargs={'pk': pk}), }) if not new: ctx.update({ - 'geometry_url': '/api/editor/geometries/?section='+str(obj.primary_section_pk), + 'geometry_url': '/api/editor/geometries/?level='+str(obj.primary_level_pk), 'on_top_of': obj.on_top_of, }) elif on_top_of: ctx.update({ - 'geometry_url': '/api/editor/geometries/?section=' + str(on_top_of.pk), + 'geometry_url': '/api/editor/geometries/?level=' + str(on_top_of.pk), 'on_top_of': on_top_of, - 'back_url': reverse('editor.sections.detail', kwargs={'pk': on_top_of.pk}), + 'back_url': reverse('editor.levels.detail', kwargs={'pk': on_top_of.pk}), }) elif model == Space and not new: - section = obj.section + level = obj.level ctx.update({ - 'section': obj.section, - 'back_url': reverse('editor.spaces.detail', kwargs={'section': obj.section.pk, 'pk': pk}), + 'level': obj.level, + 'back_url': reverse('editor.spaces.detail', kwargs={'level': obj.level.pk, 'pk': pk}), 'geometry_url': '/api/editor/geometries/?space='+pk, }) elif model == Space and new: ctx.update({ - 'section': section, - 'back_url': reverse('editor.spaces.list', kwargs={'section': section.pk}), - 'geometry_url': '/api/editor/geometries/?section='+str(section.primary_section_pk), + 'level': level, + 'back_url': reverse('editor.spaces.list', kwargs={'level': level.pk}), + 'geometry_url': '/api/editor/geometries/?level='+str(level.primary_level_pk), }) - elif hasattr(model, 'section'): + elif hasattr(model, 'level'): if obj: - section = obj.section + level = obj.level ctx.update({ - 'section': section, - 'back_url': reverse('editor.'+related_name+'.list', kwargs={'section': section.pk}), - 'geometry_url': '/api/editor/geometries/?section='+str(section.primary_section_pk), + 'level': level, + 'back_url': reverse('editor.'+related_name+'.list', kwargs={'level': level.pk}), + 'geometry_url': '/api/editor/geometries/?level='+str(level.primary_level_pk), }) elif hasattr(model, 'space'): if obj: space = obj.space ctx.update({ - 'section': space.section, + 'level': space.level, 'back_url': reverse('editor.'+related_name+'.list', kwargs={'space': space.pk}), 'geometry_url': '/api/editor/geometries/?space='+str(space.pk), }) else: kwargs = {} - if section is not None: - kwargs.update({'section': section}) + if level is not None: + kwargs.update({'level': level}) elif space is not None: kwargs.update({'space': space}) @@ -181,12 +181,12 @@ def edit(request, pk=None, model=None, section=None, space=None, on_top_of=None, # todo: suggest changes raise NotImplementedError obj.delete() - if model == Section: + if model == Level: if obj.on_top_of_id is not None: - return redirect(reverse('editor.sections.detail', kwargs={'pk': obj.on_top_of_id})) + return redirect(reverse('editor.levels.detail', kwargs={'pk': obj.on_top_of_id})) return redirect(reverse('editor.index')) elif model == Space: - return redirect(reverse('editor.spaces.list', kwargs={'section': obj.section.pk})) + return redirect(reverse('editor.spaces.list', kwargs={'level': obj.level.pk})) return redirect(ctx['back_url']) ctx['obj_title'] = obj.title return render(request, 'editor/delete.html', ctx) @@ -213,8 +213,8 @@ def edit(request, pk=None, model=None, section=None, space=None, on_top_of=None, # todo: suggest changes raise NotImplementedError - if section is not None: - obj.section = section + if level is not None: + obj.level = level if space is not None: obj.space = space @@ -237,7 +237,7 @@ def edit(request, pk=None, model=None, section=None, space=None, on_top_of=None, @sidebar_view -def list_objects(request, model=None, section=None, space=None, explicit_edit=False): +def list_objects(request, model=None, level=None, space=None, explicit_edit=False): model = EDITOR_FORM_MODELS[model] if not request.resolver_match.url_name.endswith('.list'): raise ValueError('url_name does not end with .list') @@ -254,25 +254,25 @@ def list_objects(request, model=None, section=None, space=None, explicit_edit=Fa queryset = model.objects.all().order_by('id') reverse_kwargs = {} - if section is not None: - reverse_kwargs['section'] = section - section = get_object_or_404(Section, pk=section) - queryset = queryset.filter(section=section) + if level is not None: + reverse_kwargs['level'] = level + level = get_object_or_404(Level, pk=level) + queryset = queryset.filter(level=level) ctx.update({ - 'back_url': reverse('editor.sections.detail', kwargs={'pk': section.pk}), - 'back_title': _('back to section'), - 'sections': Section.objects.filter(on_top_of__isnull=True), - 'section': section, - 'section_url': request.resolver_match.url_name, - 'geometry_url': '/api/editor/geometries/?section='+str(section.primary_section_pk), + 'back_url': reverse('editor.levels.detail', kwargs={'pk': level.pk}), + 'back_title': _('back to level'), + 'levels': Level.objects.filter(on_top_of__isnull=True), + 'level': level, + 'level_url': request.resolver_match.url_name, + 'geometry_url': '/api/editor/geometries/?level='+str(level.primary_level_pk), }) elif space is not None: reverse_kwargs['space'] = space space = get_object_or_404(Space, pk=space) queryset = queryset.filter(space=space) ctx.update({ - 'section': space.section, - 'back_url': reverse('editor.spaces.detail', kwargs={'section': space.section.pk, 'pk': space.pk}), + 'level': space.level, + 'back_url': reverse('editor.spaces.detail', kwargs={'level': space.level.pk, 'pk': space.pk}), 'back_title': _('back to space'), 'geometry_url': '/api/editor/geometries/?space='+str(space.pk), }) diff --git a/src/c3nav/mapdata/api.py b/src/c3nav/mapdata/api.py index 361f8935..64f208ae 100644 --- a/src/c3nav/mapdata/api.py +++ b/src/c3nav/mapdata/api.py @@ -12,24 +12,24 @@ from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet, ReadOnlyModelViewSet from c3nav.mapdata.models import Building, Door, Hole, LocationGroup, Source, Space -from c3nav.mapdata.models.geometry.section import SECTION_MODELS +from c3nav.mapdata.models.geometry.level import LEVEL_MODELS from c3nav.mapdata.models.geometry.space import SPACE_MODELS, Area, Column, LineObstacle, Obstacle, Point, Stair +from c3nav.mapdata.models.level import Level from c3nav.mapdata.models.locations import LOCATION_MODELS, Location, LocationRedirect, LocationSlug -from c3nav.mapdata.models.section import Section class MapdataViewSet(ReadOnlyModelViewSet): def list(self, request, *args, **kwargs): qs = self.get_queryset() geometry = ('geometry' in request.GET) - if qs.model in SECTION_MODELS and 'section' in request.GET: - if not request.GET['section'].isdigit(): - raise ValidationError(detail={'detail': _('%s is not an integer.') % 'section'}) + if qs.model in LEVEL_MODELS and 'level' in request.GET: + if not request.GET['level'].isdigit(): + raise ValidationError(detail={'detail': _('%s is not an integer.') % 'level'}) try: - section = Section.objects.get(pk=request.GET['section']) - except Section.DoesNotExist: - raise NotFound(detail=_('section not found.')) - qs = qs.filter(section=section) + level = Level.objects.get(pk=request.GET['level']) + except Level.DoesNotExist: + raise NotFound(detail=_('level not found.')) + qs = qs.filter(level=level) if qs.model in SPACE_MODELS and 'space' in request.GET: if not request.GET['space'].isdigit(): raise ValidationError(detail={'detail': _('%s is not an integer.') % 'space'}) @@ -38,17 +38,17 @@ class MapdataViewSet(ReadOnlyModelViewSet): except Space.DoesNotExist: raise NotFound(detail=_('space not found.')) qs = qs.filter(space=space) - if qs.model == Section and 'on_top_of' in request.GET: + if qs.model == Level and 'on_top_of' in request.GET: if request.GET['on_top_of'] == 'null': qs = qs.filter(on_top_of__isnull=False) else: if not request.GET['on_top_of'].isdigit(): raise ValidationError(detail={'detail': _('%s is not null or an integer.') % 'on_top_of'}) try: - section = Section.objects.get(pk=request.GET['on_top_of']) - except Section.DoesNotExist: - raise NotFound(detail=_('section not found.')) - qs = qs.filter(on_top_of=section) + level = Level.objects.get(pk=request.GET['on_top_of']) + except Level.DoesNotExist: + raise NotFound(detail=_('level not found.')) + qs = qs.filter(on_top_of=level) return Response([obj.serialize(geometry=geometry) for obj in qs.order_by('id')]) def retrieve(self, request, *args, **kwargs): @@ -61,28 +61,28 @@ class MapdataViewSet(ReadOnlyModelViewSet): ]) -class SectionViewSet(MapdataViewSet): +class LevelViewSet(MapdataViewSet): """ Add ?on_top_of=null or ?on_top_of= to filter by on_top_of. """ - queryset = Section.objects.all() + queryset = Level.objects.all() @list_route(methods=['get']) def geometrytypes(self, request): - return self.list_types(SECTION_MODELS) + return self.list_types(LEVEL_MODELS) @detail_route(methods=['get']) def svg(self, requests, pk=None): - section = self.get_object() - response = HttpResponse(section.render_svg(), 'image/svg+xml') + level = self.get_object() + response = HttpResponse(level.render_svg(), 'image/svg+xml') return response class BuildingViewSet(MapdataViewSet): - """ Add ?geometry=1 to get geometries, add ?section= to filter by section. """ + """ Add ?geometry=1 to get geometries, add ?level= to filter by level. """ queryset = Building.objects.all() class SpaceViewSet(MapdataViewSet): - """ Add ?geometry=1 to get geometries, add ?section= to filter by section. """ + """ Add ?geometry=1 to get geometries, add ?level= to filter by level. """ queryset = Space.objects.all() @list_route(methods=['get']) @@ -91,7 +91,7 @@ class SpaceViewSet(MapdataViewSet): class DoorViewSet(MapdataViewSet): - """ Add ?geometry=1 to get geometries, add ?section= to filter by section. """ + """ Add ?geometry=1 to get geometries, add ?level= to filter by level. """ queryset = Door.objects.all() diff --git a/src/c3nav/mapdata/apps.py b/src/c3nav/mapdata/apps.py index c994559e..a84d21fe 100644 --- a/src/c3nav/mapdata/apps.py +++ b/src/c3nav/mapdata/apps.py @@ -33,8 +33,8 @@ class MapdataConfig(AppConfig): from c3nav.mapdata.models.locations import Location, LOCATION_MODELS LOCATION_MODELS.extend(self._get_submodels(Location)) - from c3nav.mapdata.models.geometry.section import SectionGeometryMixin, SECTION_MODELS - SECTION_MODELS.extend(self._get_submodels(SectionGeometryMixin)) + from c3nav.mapdata.models.geometry.level import LevelGeometryMixin, LEVEL_MODELS + LEVEL_MODELS.extend(self._get_submodels(LevelGeometryMixin)) from c3nav.mapdata.models.geometry.space import SpaceGeometryMixin, SPACE_MODELS SPACE_MODELS.extend(self._get_submodels(SpaceGeometryMixin)) diff --git a/src/c3nav/mapdata/migrations/0012_rename_section_to_level.py b/src/c3nav/mapdata/migrations/0012_rename_section_to_level.py new file mode 100644 index 00000000..58e197ef --- /dev/null +++ b/src/c3nav/mapdata/migrations/0012_rename_section_to_level.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.2 on 2017-06-11 12:06 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('mapdata', '0011_outside_only_outside'), + ] + + operations = [ + migrations.RenameModel( + old_name='Section', + new_name='Level', + ), + migrations.AlterModelOptions( + name='level', + options={'ordering': ['altitude'], 'verbose_name': 'Level', 'verbose_name_plural': 'Levels'}, + ), + migrations.RenameField( + model_name='building', + old_name='section', + new_name='level', + ), + migrations.RenameField( + model_name='door', + old_name='section', + new_name='level', + ), + migrations.RenameField( + model_name='space', + old_name='section', + new_name='level', + ), + migrations.AlterField( + model_name='level', + name='altitude', + field=models.DecimalField(decimal_places=2, max_digits=6, unique=True, verbose_name='level altitude'), + ), + migrations.AlterField( + model_name='level', + name='groups', + field=models.ManyToManyField(blank=True, related_name='levels', to='mapdata.LocationGroup', + verbose_name='Location Groups'), + ), + migrations.AlterField( + model_name='level', + name='locationslug_ptr', + field=models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, + primary_key=True, related_name='levels', serialize=False, + to='mapdata.LocationSlug'), + ), + migrations.AlterField( + model_name='level', + name='on_top_of', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, + related_name='levels_on_top', to='mapdata.Level', verbose_name='on top of'), + ), + migrations.AlterField( + model_name='building', + name='level', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='buildings', + to='mapdata.Level', verbose_name='level'), + ), + migrations.AlterField( + model_name='door', + name='level', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='doors', + to='mapdata.Level', verbose_name='level'), + ), + migrations.AlterField( + model_name='space', + name='level', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='spaces', + to='mapdata.Level', verbose_name='level'), + ), + ] diff --git a/src/c3nav/mapdata/models/__init__.py b/src/c3nav/mapdata/models/__init__.py index e6458bcd..0c1711dc 100644 --- a/src/c3nav/mapdata/models/__init__.py +++ b/src/c3nav/mapdata/models/__init__.py @@ -1,5 +1,5 @@ -from .section import Section # noqa +from .level import Level # noqa from .source import Source # noqa -from c3nav.mapdata.models.geometry.section import Building, Space, Door # noqa +from c3nav.mapdata.models.geometry.level import Building, Space, Door # noqa from c3nav.mapdata.models.geometry.space import Area, Stair, Obstacle, LineObstacle, Hole # noqa from .locations import Location, LocationSlug, LocationGroup # noqa diff --git a/src/c3nav/mapdata/models/base.py b/src/c3nav/mapdata/models/base.py index bc05c83c..54d1b2d7 100644 --- a/src/c3nav/mapdata/models/base.py +++ b/src/c3nav/mapdata/models/base.py @@ -57,7 +57,7 @@ class BoundsMixin(SerializableMixin, models.Model): return ((float(result['bottom__min']), float(result['left__min'])), (float(result['top__max']), float(result['right__max']))) - def _serialize(self, section=True, **kwargs): + def _serialize(self, level=True, **kwargs): result = super()._serialize(**kwargs) result['bounds'] = self.bounds return result diff --git a/src/c3nav/mapdata/models/geometry/section.py b/src/c3nav/mapdata/models/geometry/level.py similarity index 79% rename from src/c3nav/mapdata/models/geometry/section.py rename to src/c3nav/mapdata/models/geometry/level.py index 55606003..c7df7081 100644 --- a/src/c3nav/mapdata/models/geometry/section.py +++ b/src/c3nav/mapdata/models/geometry/level.py @@ -5,18 +5,18 @@ from c3nav.mapdata.fields import GeometryField from c3nav.mapdata.models.geometry.base import GeometryMixin from c3nav.mapdata.models.locations import SpecificLocation -SECTION_MODELS = [] +LEVEL_MODELS = [] -class SectionGeometryMixin(GeometryMixin): - section = models.ForeignKey('mapdata.Section', on_delete=models.CASCADE, verbose_name=_('section')) +class LevelGeometryMixin(GeometryMixin): + level = models.ForeignKey('mapdata.Level', on_delete=models.CASCADE, verbose_name=_('level')) class Meta: abstract = True def get_geojson_properties(self) -> dict: result = super().get_geojson_properties() - result['section'] = self.section_id + result['level'] = self.level_id if hasattr(self, 'get_color'): color = self.get_color() if color: @@ -25,14 +25,14 @@ class SectionGeometryMixin(GeometryMixin): result['opacity'] = self.opacity return result - def _serialize(self, section=True, **kwargs): + def _serialize(self, level=True, **kwargs): result = super()._serialize(**kwargs) - if section: - result['section'] = self.section.id + if level: + result['level'] = self.level_id return result -class Building(SectionGeometryMixin, models.Model): +class Building(LevelGeometryMixin, models.Model): """ The outline of a building on a specific level """ @@ -44,9 +44,9 @@ class Building(SectionGeometryMixin, models.Model): default_related_name = 'buildings' -class Space(SpecificLocation, SectionGeometryMixin, models.Model): +class Space(SpecificLocation, LevelGeometryMixin, models.Model): """ - An accessible space. Shouldn't overlap with spaces on same section. + An accessible space. Shouldn't overlap with spaces on the same level. """ CATEGORIES = ( ('normal', _('normal')), @@ -81,7 +81,7 @@ class Space(SpecificLocation, SectionGeometryMixin, models.Model): return color -class Door(SectionGeometryMixin, models.Model): +class Door(LevelGeometryMixin, models.Model): """ A connection between two spaces """ diff --git a/src/c3nav/mapdata/models/section.py b/src/c3nav/mapdata/models/level.py similarity index 79% rename from src/c3nav/mapdata/models/section.py rename to src/c3nav/mapdata/models/level.py index 62a93e92..8daa9074 100644 --- a/src/c3nav/mapdata/models/section.py +++ b/src/c3nav/mapdata/models/level.py @@ -11,18 +11,18 @@ from c3nav.mapdata.models.locations import SpecificLocation from c3nav.mapdata.utils.svg import SVGImage -class Section(SpecificLocation, EditorFormMixin, models.Model): +class Level(SpecificLocation, EditorFormMixin, models.Model): """ - A map section like a level + A map level """ - altitude = models.DecimalField(_('section altitude'), null=False, unique=True, max_digits=6, decimal_places=2) - on_top_of = models.ForeignKey('mapdata.Section', null=True, on_delete=models.CASCADE, - related_name='sections_on_top', verbose_name=_('on top of')) + altitude = models.DecimalField(_('level altitude'), null=False, unique=True, max_digits=6, decimal_places=2) + on_top_of = models.ForeignKey('mapdata.Level', null=True, on_delete=models.CASCADE, + related_name='levels_on_top', verbose_name=_('on top of')) class Meta: - verbose_name = _('Section') - verbose_name_plural = _('Sections') - default_related_name = 'sections' + verbose_name = _('Level') + verbose_name_plural = _('Levels') + default_related_name = 'levels' ordering = ['altitude'] def __init__(self, *args, **kwargs): @@ -31,32 +31,32 @@ class Section(SpecificLocation, EditorFormMixin, models.Model): def lower(self): if self.on_top_of is not None: raise TypeError - return Section.objects.filter(altitude__lt=self.altitude, on_top_of__isnull=True).order_by('-altitude') + return Level.objects.filter(altitude__lt=self.altitude, on_top_of__isnull=True).order_by('-altitude') def higher(self): if self.on_top_of is not None: raise TypeError - return Section.objects.filter(altitude__gt=self.altitude, on_top_of__isnull=True).order_by('altitude') + return Level.objects.filter(altitude__gt=self.altitude, on_top_of__isnull=True).order_by('altitude') @property - def subsections(self): + def sublevels(self): if self.on_top_of is not None: raise TypeError - return chain((self, ), self.sections_on_top.all()) + return chain((self, ), self.levels_on_top.all()) @property - def subsection_title(self): + def sublevel_title(self): return '-' if self.on_top_of_id is None else self.title @property - def primary_section(self): + def primary_level(self): return self if self.on_top_of_id is None else self.on_top_of @property - def primary_section_pk(self): + def primary_level_pk(self): return self.pk if self.on_top_of_id is None else self.on_top_of_id - def _serialize(self, section=True, **kwargs): + def _serialize(self, level=True, **kwargs): result = super()._serialize(**kwargs) result['altitude'] = float(str(self.altitude)) return result @@ -109,10 +109,10 @@ class Section(SpecificLocation, EditorFormMixin, models.Model): # draw space background doors = self.doors.all() door_geometries = cascaded_union(tuple(d.geometry for d in doors)) - section_geometry = cascaded_union((space_geometries, building_geometries, door_geometries)) - section_geometry = section_geometry.difference(hole_geometries) - section_clip = svg.register_geometry(section_geometry, defid='section', as_clip_path=True) - svg.add_geometry(fill_color='#d1d1d1', clip_path=section_clip) + level_geometry = cascaded_union((space_geometries, building_geometries, door_geometries)) + level_geometry = level_geometry.difference(hole_geometries) + level_clip = svg.register_geometry(level_geometry, defid='level', as_clip_path=True) + svg.add_geometry(fill_color='#d1d1d1', clip_path=level_clip) # color in spaces spaces_by_color = {} @@ -134,7 +134,7 @@ class Section(SpecificLocation, EditorFormMixin, models.Model): if effects: wall_dilated_geometry = wall_geometry.buffer(0.7, join_style=JOIN_STYLE.mitre) svg.add_geometry(wall_dilated_geometry, fill_color='#000000', opacity=0.1, filter='wallblur', - clip_path=section_clip) + clip_path=level_clip) for space in spaces: self._render_space_inventory(svg, space) diff --git a/src/c3nav/mapdata/models/locations.py b/src/c3nav/mapdata/models/locations.py index 19a05591..b336994c 100644 --- a/src/c3nav/mapdata/models/locations.py +++ b/src/c3nav/mapdata/models/locations.py @@ -13,8 +13,8 @@ LOCATION_MODELS = [] class LocationSlug(SerializableMixin, models.Model): LOCATION_TYPE_CODES = { - 'Section': 'se', - 'Space': 'sp', + 'Level': 'l', + 'Space': 's', 'Area': 'a', 'Point': 'p', 'LocationGroup': 'g' diff --git a/src/c3nav/mapdata/models/source.py b/src/c3nav/mapdata/models/source.py index 8485f7b1..bf232ec2 100644 --- a/src/c3nav/mapdata/models/source.py +++ b/src/c3nav/mapdata/models/source.py @@ -20,7 +20,7 @@ class Source(EditorFormMixin, BoundsMixin, models.Model): def title(self): return self.name - def _serialize(self, section=True, **kwargs): + def _serialize(self, level=True, **kwargs): result = super()._serialize(**kwargs) result['name'] = self.name return result diff --git a/src/c3nav/routing/graph.py b/src/c3nav/routing/graph.py index 34c5e869..e0607adc 100644 --- a/src/c3nav/routing/graph.py +++ b/src/c3nav/routing/graph.py @@ -9,8 +9,8 @@ from django.conf import settings from scipy.sparse.csgraph._shortest_path import shortest_path from scipy.sparse.csgraph._tools import csgraph_from_dense +from c3nav.mapdata.models.level import Level from c3nav.mapdata.models.locations import Location, LocationGroup -from c3nav.mapdata.models.section import Section from c3nav.routing.connection import GraphConnection from c3nav.routing.exceptions import AlreadyThere, NoRouteFound, NotYetRoutable from c3nav.routing.level import GraphLevel @@ -28,7 +28,7 @@ class Graph: def __init__(self, mtime=None): self.mtime = mtime self.levels = OrderedDict() - for level in Section.objects.all(): + for level in Level.objects.all(): self.levels[level.name] = GraphLevel(self, level) self.points = [] diff --git a/src/c3nav/site/views.py b/src/c3nav/site/views.py index f9767b34..711d0b6f 100644 --- a/src/c3nav/site/views.py +++ b/src/c3nav/site/views.py @@ -8,7 +8,7 @@ from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils import timezone -from c3nav.mapdata.models.section import Section +from c3nav.mapdata.models.level import Level ctype_mapping = { 'yes': ('up', 'down'), @@ -232,7 +232,7 @@ def main(request, location=None, origin=None, destination=None): def map_image(request, area, level): - level = get_object_or_404(Section, name=level, intermediate=False) + level = get_object_or_404(Level, name=level, intermediate=False) if area == ':base': img = get_render_path('png', level.name, 'full', True) elif area == ':full':