add AreaOfInterest and GroupOfInterest

This commit is contained in:
Laura Klünder 2016-12-12 13:21:09 +01:00
parent 52958ec5fc
commit 9c202ab6cd
11 changed files with 194 additions and 13 deletions

View file

@ -61,6 +61,10 @@ class MapitemFormMixin(ModelForm):
# set field_name # set field_name
self.fields['levels'].to_field_name = 'name' self.fields['levels'].to_field_name = 'name'
if 'groups' in self.fields:
# set field_name
self.fields['groups'].to_field_name = 'name'
if 'geometry' in self.fields: if 'geometry' in self.fields:
# hide geometry widget # hide geometry widget
self.fields['geometry'].widget = HiddenInput() self.fields['geometry'].widget = HiddenInput()
@ -94,12 +98,17 @@ class MapitemFormMixin(ModelForm):
if 'geometry' in self.fields: if 'geometry' in self.fields:
if not self.cleaned_data.get('geometry'): if not self.cleaned_data.get('geometry'):
raise ValidationError('Missing geometry.') raise ValidationError('Missing geometry.')
if hasattr(self.instance, 'titles') and not any(self.titles.values()):
raise ValidationError(
_('You have to select a title in at least one language.')
)
super().clean() super().clean()
def create_editor_form(mapitemtype): def create_editor_form(mapitemtype):
possible_fields = ['name', 'package', 'altitude', 'level', 'intermediate', 'levels', 'geometry', possible_fields = ['name', 'package', 'altitude', 'level', 'intermediate', 'levels', 'geometry',
'elevator', 'button', 'crop_to_level', 'width'] 'elevator', 'button', 'crop_to_level', 'width', 'groups']
existing_fields = [field for field in possible_fields if hasattr(mapitemtype, field)] existing_fields = [field for field in possible_fields if hasattr(mapitemtype, field)]
class EditorForm(MapitemFormMixin, ModelForm): class EditorForm(MapitemFormMixin, ModelForm):

View file

@ -29,7 +29,7 @@ form button.invisiblesubmit {
display: block; display: block;
} }
.leaflet-control-layers-toggle { .leaflet-container .leaflet-control-layers-toggle {
color:#000000 !important; color:#000000 !important;
font-size:14px; font-size:14px;
text-align:center; text-align:center;
@ -38,7 +38,7 @@ form button.invisiblesubmit {
background-image:none; background-image:none;
padding:0 8px; padding:0 8px;
} }
.leaflet-control-layers-expanded { .leaflet-container .leaflet-control-layers-expanded {
min-width:75px; min-width:75px;
} }
.leaflet-levels { .leaflet-levels {

View file

@ -281,7 +281,8 @@ editor = {
'elevatorlevel': '#9EF8FB', 'elevatorlevel': '#9EF8FB',
'levelconnector': '#FFFF00', 'levelconnector': '#FFFF00',
'shadow': '#000000', 'shadow': '#000000',
'stair': '#FF0000' 'stair': '#FF0000',
'areaofinterest': '#0099FF'
}, },
_line_draw_geometry_style: function(style) { _line_draw_geometry_style: function(style) {
style.stroke = true; style.stroke = true;
@ -300,12 +301,19 @@ editor = {
}, },
_get_mapitem_type_style: function (mapitem_type) { _get_mapitem_type_style: function (mapitem_type) {
// get styles for a specific mapitem // get styles for a specific mapitem
return { var result = {
stroke: false, stroke: false,
fillColor: editor._geometry_colors[mapitem_type], fillColor: editor._geometry_colors[mapitem_type],
fillOpacity: 0.6, fillOpacity: (mapitem_type == 'areaofinterest') ? 0.2 : 0.6,
smoothFactor: 0 smoothFactor: 0
}; };
if (mapitem_type == 'areaofinterest') {
result.fillOpacity = 0.02;
result.color = result.fillColor;
result.stroke = true;
result.weight = 1;
}
return result;
}, },
_register_geojson_feature: function (feature, layer) { _register_geojson_feature: function (feature, layer) {
// onEachFeature callback for GeoJSON loader register all needed events // onEachFeature callback for GeoJSON loader register all needed events

View file

@ -13,7 +13,8 @@
</tr> </tr>
{% endif %} {% endif %}
<tr data-name="{{ item.name }}"> <tr data-name="{{ item.name }}">
<td>{{ item.name }}</td>
<td>{% if item.title != item.name %}{{ item.title }} <small>{{ item.name }}</small>{% else %}{{ item.name }}{% endif %}</td>
{% if has_elevator %} {% if has_elevator %}
<td><a href="{% url 'editor.mapitems.edit' mapitem_type='elevator' name=item.elevator.name %}">{{ item.elevator }}</a></td> <td><a href="{% url 'editor.mapitems.edit' mapitem_type='elevator' name=item.elevator.name %}">{{ item.elevator }}</a></td>
{% endif %} {% endif %}

View file

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.4 on 2016-12-12 12:05
from __future__ import unicode_literals
import c3nav.mapdata.fields
import c3nav.mapdata.models.interest
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mapdata', '0017_auto_20161208_2039'),
]
operations = [
migrations.CreateModel(
name='AreaOfInterest',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.SlugField(unique=True, verbose_name='Name')),
('geometry', c3nav.mapdata.fields.GeometryField()),
('titles', c3nav.mapdata.fields.JSONField()),
],
options={
'default_related_name': 'areasofinterest',
'verbose_name_plural': 'Areas of Interest',
'verbose_name': 'Area of Interest',
},
bases=(models.Model, c3nav.mapdata.models.interest.MapItemOfInterestMixin),
),
migrations.CreateModel(
name='GroupOfInterest',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.SlugField(unique=True, verbose_name='Name')),
('titles', c3nav.mapdata.fields.JSONField()),
('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='groupsofinterest', to='mapdata.Package', verbose_name='map package')),
],
options={
'default_related_name': 'groupsofinterest',
'verbose_name_plural': 'Groups of Interest',
'verbose_name': 'Group of Interest',
},
bases=(models.Model, c3nav.mapdata.models.interest.MapItemOfInterestMixin),
),
migrations.AddField(
model_name='areaofinterest',
name='groups',
field=models.ManyToManyField(blank=True, related_name='areasofinterest', to='mapdata.GroupOfInterest', verbose_name='Groups of Interest'),
),
migrations.AddField(
model_name='areaofinterest',
name='level',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='areasofinterest', to='mapdata.Level', verbose_name='level'),
),
migrations.AddField(
model_name='areaofinterest',
name='package',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='areasofinterest', to='mapdata.Package', verbose_name='map package'),
),
]

View file

@ -3,3 +3,4 @@ from .package import Package # noqa
from .source import Source # noqa from .source import Source # noqa
from .collections import Elevator # noqa from .collections import Elevator # noqa
from .geometry import GeometryMapItemWithLevel, GEOMETRY_MAPITEM_TYPES # noqa from .geometry import GeometryMapItemWithLevel, GEOMETRY_MAPITEM_TYPES # noqa
from .interest import AreaOfInterest, GroupOfInterest # noqa

View file

@ -3,6 +3,7 @@ from collections import OrderedDict
from django.db import models from django.db import models
from django.db.models.base import ModelBase from django.db.models.base import ModelBase
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import get_language
from c3nav.mapdata.lastupdate import set_last_mapdata_update from c3nav.mapdata.lastupdate import set_last_mapdata_update
@ -23,6 +24,15 @@ class MapItem(models.Model, metaclass=MapItemMeta):
EditorForm = None EditorForm = None
@property
def title(self):
if not hasattr(self, 'titles'):
return self.name
lang = get_language()
if lang in self.titles:
return self.titles[lang]
return next(iter(self.titles.values())) if self.titles else self.name
@classmethod @classmethod
def get_path_prefix(cls): def get_path_prefix(cls):
return cls._meta.default_related_name + '/' return cls._meta.default_related_name + '/'

View file

@ -32,10 +32,6 @@ class GeometryMapItem(MapItem, metaclass=GeometryMapItemMeta):
class Meta: class Meta:
abstract = True abstract = True
@property
def title(self):
return self.name
@classmethod @classmethod
def fromfile(cls, data, file_path): def fromfile(cls, data, file_path):
kwargs = super().fromfile(data, file_path) kwargs = super().fromfile(data, file_path)

View file

@ -0,0 +1,82 @@
from collections import OrderedDict
from django.db import models
from django.utils.translation import ugettext_lazy as _
from c3nav.mapdata.fields import JSONField
from c3nav.mapdata.models.base import MapItem
from c3nav.mapdata.models.geometry import GeometryMapItemWithLevel
# noinspection PyUnresolvedReferences
class MapItemOfInterestMixin:
def get_geojson_properties(self):
result = super().get_geojson_properties()
result['titles'] = OrderedDict(sorted(self.titles.items()))
return result
@classmethod
def fromfile(cls, data, file_path):
kwargs = super().fromfile(data, file_path)
if 'titles' not in data:
raise ValueError('missing titles.')
titles = data['titles']
if not isinstance(titles, dict):
raise ValueError('Invalid titles format.')
if any(not isinstance(lang, str) for lang in titles.keys()):
raise ValueError('titles: All languages have to be strings.')
if any(not isinstance(title, str) for title in titles.values()):
raise ValueError('titles: All titles have to be strings.')
if any(not title for title in titles.values()):
raise ValueError('titles: Titles must not be empty strings.')
kwargs['titles'] = titles
return kwargs
def tofile(self):
result = super().tofile()
result['titles'] = OrderedDict(sorted(self.titles.items()))
return result
class GroupOfInterest(MapItem, MapItemOfInterestMixin):
titles = JSONField()
class Meta:
verbose_name = _('Group of Interest')
verbose_name_plural = _('Groups of Interest')
default_related_name = 'groupsofinterest'
class AreaOfInterest(GeometryMapItemWithLevel, MapItemOfInterestMixin):
titles = JSONField()
groups = models.ManyToManyField(GroupOfInterest, verbose_name=_('Groups of Interest'), blank=True)
geomtype = 'polygon'
class Meta:
verbose_name = _('Area of Interest')
verbose_name_plural = _('Areas of Interest')
default_related_name = 'areasofinterest'
@classmethod
def fromfile(cls, data, file_path):
kwargs = super().fromfile(data, file_path)
groups = data.get('groups', [])
if not isinstance(groups, list):
raise TypeError('groups has to be a list')
kwargs['groups'] = groups
return kwargs
def get_geojson_properties(self):
result = super().get_geojson_properties()
result['groups'] = tuple(self.groups.all().order_by('name').values_list('name', flat=True))
return result
def tofile(self):
result = super().tofile()
result['groups'] = sorted(self.groups.all().order_by('name').values_list('name', flat=True))
result.move_to_end('geometry')
return result

View file

@ -1,7 +1,8 @@
from c3nav.mapdata.models import Level, Package, Source from c3nav.mapdata.models import AreaOfInterest, GroupOfInterest, Level, Package, Source
from c3nav.mapdata.models.collections import Elevator from c3nav.mapdata.models.collections import Elevator
from c3nav.mapdata.models.geometry import (Building, Door, ElevatorLevel, Hole, LevelConnector, LineObstacle, Obstacle, from c3nav.mapdata.models.geometry import (Building, Door, ElevatorLevel, Hole, LevelConnector, LineObstacle, Obstacle,
Outside, Room, Stair) Outside, Room, Stair)
ordered_models = (Package, Level, LevelConnector, Source, Building, Room, Outside, Door, Obstacle, Hole) ordered_models = (Package, Level, LevelConnector, Source, Building, Room, Outside, Door, Obstacle, Hole)
ordered_models += (Elevator, ElevatorLevel, LineObstacle, Stair) ordered_models += (Elevator, ElevatorLevel, LineObstacle, Stair)
ordered_models += (GroupOfInterest, AreaOfInterest)

View file

@ -7,7 +7,7 @@ from collections import OrderedDict
from django.conf import settings from django.conf import settings
from django.core.management import CommandError from django.core.management import CommandError
from c3nav.mapdata.models import Elevator, Level, Package from c3nav.mapdata.models import AreaOfInterest, Elevator, GroupOfInterest, Level, Package
from c3nav.mapdata.models.geometry import LevelConnector from c3nav.mapdata.models.geometry import LevelConnector
from c3nav.mapdata.packageio.const import ordered_models from c3nav.mapdata.packageio.const import ordered_models
@ -165,6 +165,11 @@ class ReaderItem:
levels = [self.reader.saved_items[Level][name].obj.pk for name in self.data['levels']] levels = [self.reader.saved_items[Level][name].obj.pk for name in self.data['levels']]
self.data.pop('levels') self.data.pop('levels')
groups = []
if self.model == AreaOfInterest:
groups = [self.reader.saved_items[GroupOfInterest][name].obj.pk for name in self.data['groups']]
self.data.pop('groups')
# Change name references to the referenced object # Change name references to the referenced object
for name, model in self.relations.items(): for name, model in self.relations.items():
if name in self.data: if name in self.data:
@ -186,3 +191,8 @@ class ReaderItem:
self.obj.levels.clear() self.obj.levels.clear()
for level in levels: for level in levels:
self.obj.levels.add(level) self.obj.levels.add(level)
if groups:
self.obj.groups.clear()
for group in groups:
self.obj.groups.add(group)