add AreaOfInterest and GroupOfInterest
This commit is contained in:
parent
52958ec5fc
commit
9c202ab6cd
11 changed files with 194 additions and 13 deletions
|
@ -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):
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
63
src/c3nav/mapdata/migrations/0018_auto_20161212_1205.py
Normal file
63
src/c3nav/mapdata/migrations/0018_auto_20161212_1205.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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
|
||||||
|
|
|
@ -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 + '/'
|
||||||
|
|
|
@ -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)
|
||||||
|
|
82
src/c3nav/mapdata/models/interest.py
Normal file
82
src/c3nav/mapdata/models/interest.py
Normal 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
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue