rename Section back to Level

This commit is contained in:
Laura Klünder 2017-06-11 14:43:14 +02:00
parent 27ff35e785
commit 1498b7aeb0
27 changed files with 416 additions and 334 deletions

View file

@ -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)

View file

@ -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=<id> or ?space=<id>
/geometries/ returns a list of geojson features, you have to specify ?level=<id> or ?space=<id>
/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(),)

View file

@ -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

View file

@ -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;
}

View file

@ -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('<h3>Error '+data.status+'</h3>'+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;
}
});

View file

@ -1,6 +1,6 @@
{% load bootstrap3 %}
{% load i18n %}
{% include 'editor/fragment_sections.html' %}
{% include 'editor/fragment_levels.html' %}
<h3>{% blocktrans %}Delete {{ model_title }}{% endblocktrans %}</h3>
<form action="{{ path }}" method="post">

View file

@ -1,7 +1,7 @@
{% load bootstrap3 %}
{% load i18n %}
{% include 'editor/fragment_sections.html' %}
{% include 'editor/fragment_levels.html' %}
<h3>
{% 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 %}
<small>{% blocktrans %}on top of {{ on_top_of_section_title }}{% endblocktrans %}</small>
{% with on_top_of.title as on_top_of_level_title %}
<small>{% blocktrans %}on top of {{ on_top_of_level_title }}{% endblocktrans %}</small>
{% endwith %}
{% endif %}
</h3>

View file

@ -0,0 +1,26 @@
{% if levels %}
<ul data-levels>
{% for l in levels %}
<li>
<a data-id="{{ l.pk }}" href="{% if level_as_pk %}{% url level_url pk=l.id %}{% else %}{% url level_url level=l.id %}{% endif %}"{% if level.primary_level == l %} class="current"{% endif %}>{{ l.title }}</a>
</li>
{% endfor %}
</ul>
<ul data-sublevels>
{% for l in level.primary_level.sublevels %}
<li>
<a data-id="{{ l.pk }}" href="{% if level_as_pk %}{% url level_url pk=l.id %}{% else %}{% url level_url level=l.id %}{% endif %}"{% if level == l %} class="current"{% endif %}>{{ l.sublevel_title }}</a>
</li>
{% endfor %}
</ul>
{% elif level %}
<ul data-levels>
<li><a data-id="{{ level.primary_level.pk }}" href="" class="current">{{ level.primary_level.title }}</a></li>
</ul>
<ul data-sublevels>
<li><a data-id="{{ level.pk }}" href="" class="current">{{ level.sublevel_title }}</a></li>
</ul>
{% endif %}
{% if geometry_url %}
<span data-geometry-url="{{ geometry_url }}"></span>
{% endif %}

View file

@ -1,26 +0,0 @@
{% if sections %}
<ul data-sections>
{% for s in sections %}
<li>
<a data-id="{{ s.pk }}" href="{% if section_as_pk %}{% url section_url pk=s.id %}{% else %}{% url section_url section=s.id %}{% endif %}"{% if section.primary_section == s %} class="current"{% endif %}>{{ s.title }}</a>
</li>
{% endfor %}
</ul>
<ul data-subsections>
{% for s in section.primary_section.subsections %}
<li>
<a data-id="{{ s.pk }}" href="{% if section_as_pk %}{% url section_url pk=s.id %}{% else %}{% url section_url section=s.id %}{% endif %}"{% if section == s %} class="current"{% endif %}>{{ s.subsection_title }}</a>
</li>
{% endfor %}
</ul>
{% elif section %}
<ul data-sections>
<li><a data-id="{{ section.primary_section.pk }}" href="" class="current">{{ section.primary_section.title }}</a></li>
</ul>
<ul data-subsections>
<li><a data-id="{{ section.pk }}" href="" class="current">{{ section.subsection_title }}</a></li>
</ul>
{% endif %}
{% if geometry_url %}
<span data-geometry-url="{{ geometry_url }}"></span>
{% endif %}

View file

@ -1,15 +1,15 @@
{% load bootstrap3 %}
{% load i18n %}
<a class="btn btn-default btn-sm pull-right" accesskey="n" href="{% url 'editor.sections.create' %}">
{% trans 'Section' as model_title %}
<a class="btn btn-default btn-sm pull-right" accesskey="n" href="{% url 'editor.levels.create' %}">
{% trans 'Level' as model_title %}
{% blocktrans %}New {{ model_title }}{% endblocktrans %}
</a>
<h3>{% trans 'Sections' %}</h3>
<h3>{% trans 'Level' %}</h3>
<div class="list-group">
{% for s in sections %}
<a href="{% url 'editor.sections.detail' pk=s.pk %}" class="list-group-item">
{{ s.title }}
{% for l in levels %}
<a href="{% url 'editor.levels.detail' pk=l.pk %}" class="list-group-item">
{{ l.title }}
</a>
{% endfor %}
</div>

View file

@ -0,0 +1,42 @@
{% load bootstrap3 %}
{% load i18n %}
{% include 'editor/fragment_levels.html' %}
<a class="btn btn-default btn-sm pull-right" accesskey="e" href="{% url 'editor.levels.edit' pk=level.pk %}">
{% trans 'Level' as model_title %}
{% blocktrans %}Edit {{ model_title }}{% endblocktrans %}
</a>
<h3>
<h3>
{{ level.title }}
{% if level.on_top_of != None %}
{% with level.on_top_of.title as on_top_of_level_title %}
<small>{% blocktrans %}on top of {{ on_top_of_level_title }}{% endblocktrans %}</small>
{% endwith %}
{% endif %}
</h3>
<p>
{% if level.on_top_of == None %}
<a href="{% url 'editor.index' %}">&laquo; {% trans 'back to overview' %}</a>
{% else %}
<a href="{% url 'editor.levels.detail' pk=level.on_top_of.pk %}">&laquo; {% trans 'back to parent level' %}</a>
{% endif %}
</p>
{% include 'editor/fragment_child_models.html' %}
<div class="clearfix"></div>
{% if level.on_top_of == None %}
<a class="btn btn-default btn-sm pull-right" accesskey="n" href="{% url 'editor.levels_on_top.create' on_top_of=level.pk %}">
{% blocktrans %}New {{ model_title }}{% endblocktrans %}
</a>
<h3>{% trans 'Levels on top' %}</h3>
<div class="list-group">
{% for l in levels_on_top %}
<a href="{% url 'editor.levels.detail' pk=l.pk %}" class="list-group-item">
{{ l.title }}
</a>
{% endfor %}
</div>
{% endif %}

View file

@ -1,16 +1,16 @@
{% load bootstrap3 %}
{% load i18n %}
{% include 'editor/fragment_sections.html' %}
{% include 'editor/fragment_levels.html' %}
<a class="btn btn-default btn-sm pull-right" accesskey="n" href="{{ create_url }}">
{% blocktrans %}New {{ model_title }}{% endblocktrans %}
</a>
<h3>
{% blocktrans %}{{ model_title_plural }}{% endblocktrans %}
{% if section %}
{% with section.title as section_title %}
<small>{% blocktrans %}in section {{ section_title }}{% endblocktrans %}</small>
{% if level %}
{% with level.title as level_title %}
<small>{% blocktrans %}on level {{ level_title }}{% endblocktrans %}</small>
{% endwith %}
{% endif %}
{% if space %}

View file

@ -1,41 +0,0 @@
{% load bootstrap3 %}
{% load i18n %}
{% include 'editor/fragment_sections.html' %}
<a class="btn btn-default btn-sm pull-right" accesskey="e" href="{% url 'editor.sections.edit' pk=section.pk %}">
{% trans 'Section' as model_title %}
{% blocktrans %}Edit {{ model_title }}{% endblocktrans %}
</a>
<h3>
{{ section.title }}
{% if section.on_top_of != None %}
{% with section.on_top_of.title as on_top_of_section_title %}
<small>{% blocktrans %}on top of {{ on_top_of_section_title }}{% endblocktrans %}</small>
{% endwith %}
{% endif %}
</h3>
<p>
{% if section.on_top_of == None %}
<a href="{% url 'editor.index' %}">&laquo; {% trans 'back to overview' %}</a>
{% else %}
<a href="{% url 'editor.sections.detail' pk=section.on_top_of.pk %}">&laquo; {% trans 'back to parent section' %}</a>
{% endif %}
</p>
{% include 'editor/fragment_child_models.html' %}
<div class="clearfix"></div>
{% if section.on_top_of == None %}
<a class="btn btn-default btn-sm pull-right" accesskey="n" href="{% url 'editor.sections_on_top.create' on_top_of=section.pk %}">
{% blocktrans %}New {{ model_title }}{% endblocktrans %}
</a>
<h3>{% trans 'Sections on top' %}</h3>
<div class="list-group">
{% for s in sections_on_top %}
<a href="{% url 'editor.sections.detail' pk=s.pk %}" class="list-group-item">
{{ s.title }}
</a>
{% endfor %}
</div>
{% endif %}

View file

@ -1,14 +1,14 @@
{% load bootstrap3 %}
{% load i18n %}
{% include 'editor/fragment_sections.html' %}
{% include 'editor/fragment_levels.html' %}
<a class="btn btn-default btn-sm pull-right" accesskey="e" href="{% url 'editor.spaces.edit' section=space.section.pk pk=space.pk %}">
<a class="btn btn-default btn-sm pull-right" accesskey="e" href="{% url 'editor.spaces.edit' level=space.level.pk pk=space.pk %}">
{% trans 'Space' as model_title %}
{% blocktrans %}Edit {{ model_title }}{% endblocktrans %}
</a>
<h3>{{ space.title }}</h3>
<p>
<a href="{% url 'editor.spaces.list' section=space.section.pk %}">&laquo; {% trans 'back to overview' %}</a>
<a href="{% url 'editor.spaces.list' level=space.level.pk %}">&laquo; {% trans 'back to overview' %}</a>
</p>
{% include 'editor/fragment_child_models.html' %}

View file

@ -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<pk>[0-9]+)/$', section_detail, name='editor.sections.detail'),
url(r'^sections/(?P<section>[0-9]+)/spaces/(?P<pk>[0-9]+)/$', space_detail, name='editor.spaces.detail'),
url(r'^sections/(?P<on_top_of>[0-9]+)/sections_on_top/create$', edit, name='editor.sections_on_top.create',
kwargs={'model': 'Section'}),
url(r'^levels/(?P<pk>[0-9]+)/$', level_detail, name='editor.levels.detail'),
url(r'^levels/(?P<level>[0-9]+)/spaces/(?P<pk>[0-9]+)/$', space_detail, name='editor.spaces.detail'),
url(r'^levels/(?P<on_top_of>[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'))

View file

@ -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),
})

View file

@ -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=<id> 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=<id> to filter by section. """
""" Add ?geometry=1 to get geometries, add ?level=<id> to filter by level. """
queryset = Building.objects.all()
class SpaceViewSet(MapdataViewSet):
""" Add ?geometry=1 to get geometries, add ?section=<id> to filter by section. """
""" Add ?geometry=1 to get geometries, add ?level=<id> 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=<id> to filter by section. """
""" Add ?geometry=1 to get geometries, add ?level=<id> to filter by level. """
queryset = Door.objects.all()

View file

@ -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))

View file

@ -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'),
),
]

View file

@ -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

View file

@ -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

View file

@ -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
"""

View file

@ -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)

View file

@ -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'

View file

@ -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

View file

@ -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 = []

View file

@ -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':