location subtitles
This commit is contained in:
parent
0967e0d671
commit
af2c05a13c
6 changed files with 171 additions and 3 deletions
|
@ -108,7 +108,8 @@ class MapitemFormMixin(ModelForm):
|
||||||
|
|
||||||
def create_editor_form(mapitemtype):
|
def create_editor_form(mapitemtype):
|
||||||
possible_fields = ['name', 'package', 'altitude', 'level', 'intermediate', 'levels', 'geometry', 'direction',
|
possible_fields = ['name', 'package', 'altitude', 'level', 'intermediate', 'levels', 'geometry', 'direction',
|
||||||
'elevator', 'button', 'crop_to_level', 'width', 'groups', 'override_altitude']
|
'elevator', 'button', 'crop_to_level', 'width', 'groups', 'override_altitude',
|
||||||
|
'location_type', 'can_search', 'can_describe']
|
||||||
existing_fields = [field.name for field in mapitemtype._meta.get_fields() if field.name in possible_fields]
|
existing_fields = [field.name for field in mapitemtype._meta.get_fields() if field.name in possible_fields]
|
||||||
|
|
||||||
class EditorForm(MapitemFormMixin, ModelForm):
|
class EditorForm(MapitemFormMixin, ModelForm):
|
||||||
|
|
|
@ -7,6 +7,7 @@ from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
|
|
||||||
from c3nav.editor.hosters import get_hoster_for_package, hosters
|
from c3nav.editor.hosters import get_hoster_for_package, hosters
|
||||||
|
from c3nav.mapdata.models import AreaLocation
|
||||||
from c3nav.mapdata.models.base import MAPITEM_TYPES
|
from c3nav.mapdata.models.base import MAPITEM_TYPES
|
||||||
from c3nav.mapdata.models.package import Package
|
from c3nav.mapdata.models.package import Package
|
||||||
from c3nav.mapdata.packageio.write import json_encode
|
from c3nav.mapdata.packageio.write import json_encode
|
||||||
|
@ -54,6 +55,9 @@ def list_mapitems(request, mapitem_type, level=None):
|
||||||
elif hasattr(mapitemtype, 'levels'):
|
elif hasattr(mapitemtype, 'levels'):
|
||||||
queryset = queryset.filter(levels__name=level)
|
queryset = queryset.filter(levels__name=level)
|
||||||
|
|
||||||
|
if issubclass(mapitemtype, AreaLocation):
|
||||||
|
queryset = sorted(queryset, key=AreaLocation.get_sort_key)
|
||||||
|
|
||||||
return render(request, 'editor/mapitems.html', {
|
return render(request, 'editor/mapitems.html', {
|
||||||
'mapitem_type': mapitem_type,
|
'mapitem_type': mapitem_type,
|
||||||
'title': mapitemtype._meta.verbose_name_plural,
|
'title': mapitemtype._meta.verbose_name_plural,
|
||||||
|
|
|
@ -68,6 +68,10 @@ class GeometryViewSet(ViewSet):
|
||||||
|
|
||||||
return self._list(request, types=types, level=level, packages=packages, add_cache_key=cache_key)
|
return self._list(request, types=types, level=level, packages=packages, add_cache_key=cache_key)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def compare_by_location_type(x: AreaLocation, y: AreaLocation):
|
||||||
|
return AreaLocation.LOCATION_TYPES.index(x.location_type) - AreaLocation.LOCATION_TYPES.index(y.location_type)
|
||||||
|
|
||||||
@cache_mapdata_api_response()
|
@cache_mapdata_api_response()
|
||||||
def _list(self, request, types, level, packages):
|
def _list(self, request, types, level, packages):
|
||||||
results = []
|
results = []
|
||||||
|
@ -94,6 +98,9 @@ class GeometryViewSet(ViewSet):
|
||||||
if hasattr(mapitemtype, field_name):
|
if hasattr(mapitemtype, field_name):
|
||||||
queryset.prefetch_related(field_name)
|
queryset.prefetch_related(field_name)
|
||||||
|
|
||||||
|
if issubclass(mapitemtype, AreaLocation):
|
||||||
|
queryset = sorted(queryset, key=AreaLocation.get_sort_key)
|
||||||
|
|
||||||
if issubclass(mapitemtype, DirectedLineGeometryMapItemWithLevel):
|
if issubclass(mapitemtype, DirectedLineGeometryMapItemWithLevel):
|
||||||
results.extend(obj.to_shadow_geojson() for obj in queryset)
|
results.extend(obj.to_shadow_geojson() for obj in queryset)
|
||||||
|
|
||||||
|
@ -161,8 +168,9 @@ class LocationViewSet(CachedReadOnlyViewSetMixin, ViewSet):
|
||||||
|
|
||||||
def list(self, request, **kwargs):
|
def list(self, request, **kwargs):
|
||||||
locations = []
|
locations = []
|
||||||
locations += list(filter_queryset_by_package_access(request, AreaLocation.objects.all()))
|
locations += sorted(filter_queryset_by_package_access(request, AreaLocation.objects.filter(can_search=True)),
|
||||||
locations += list(filter_queryset_by_package_access(request, LocationGroup.objects.all()))
|
key=AreaLocation.get_sort_key)
|
||||||
|
locations += list(filter_queryset_by_package_access(request, LocationGroup.objects.filter(can_search=True)))
|
||||||
return Response([location.to_location_json() for location in locations])
|
return Response([location.to_location_json() for location in locations])
|
||||||
|
|
||||||
def retrieve(self, request, name=None, **kwargs):
|
def retrieve(self, request, name=None, **kwargs):
|
||||||
|
|
36
src/c3nav/mapdata/migrations/0025_auto_20161219_1440.py
Normal file
36
src/c3nav/mapdata/migrations/0025_auto_20161219_1440.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.4 on 2016-12-19 14:40
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mapdata', '0024_oneway'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='arealocation',
|
||||||
|
name='can_describe',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='can be used to describe a position'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='arealocation',
|
||||||
|
name='can_search',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='can be searched'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='arealocation',
|
||||||
|
name='location_type',
|
||||||
|
field=models.CharField(choices=[('level', 'Level name'), ('area', 'General Area'), ('room', 'Room'), ('roomsegment', 'Room Segment'), ('poi', 'Point of Interest')], default='room', max_length=20, verbose_name='Location Type'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='locationgroup',
|
||||||
|
name='can_search',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='can be searched'),
|
||||||
|
),
|
||||||
|
]
|
20
src/c3nav/mapdata/migrations/0026_auto_20161219_1501.py
Normal file
20
src/c3nav/mapdata/migrations/0026_auto_20161219_1501.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.4 on 2016-12-19 15:01
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mapdata', '0025_auto_20161219_1440'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='arealocation',
|
||||||
|
name='location_type',
|
||||||
|
field=models.CharField(choices=[('level', 'Level'), ('area', 'General Area'), ('room', 'Room'), ('roomsegment', 'Room Segment'), ('poi', 'Point of Interest')], max_length=20, verbose_name='Location Type'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,11 +1,13 @@
|
||||||
import re
|
import re
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from django.core.cache import cache
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from c3nav.mapdata.fields import JSONField
|
from c3nav.mapdata.fields import JSONField
|
||||||
|
from c3nav.mapdata.lastupdate import get_last_mapdata_update
|
||||||
from c3nav.mapdata.models import Level
|
from c3nav.mapdata.models import Level
|
||||||
from c3nav.mapdata.models.base import MapItem
|
from c3nav.mapdata.models.base import MapItem
|
||||||
from c3nav.mapdata.models.geometry import GeometryMapItemWithLevel
|
from c3nav.mapdata.models.geometry import GeometryMapItemWithLevel
|
||||||
|
@ -67,6 +69,7 @@ class LocationModelMixin(Location):
|
||||||
|
|
||||||
class LocationGroup(LocationModelMixin, MapItem):
|
class LocationGroup(LocationModelMixin, MapItem):
|
||||||
titles = JSONField()
|
titles = JSONField()
|
||||||
|
can_search = models.BooleanField(default=True, verbose_name=_('can be searched'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Location Group')
|
verbose_name = _('Location Group')
|
||||||
|
@ -77,9 +80,42 @@ class LocationGroup(LocationModelMixin, MapItem):
|
||||||
def location_id(self):
|
def location_id(self):
|
||||||
return 'g:'+self.name
|
return 'g:'+self.name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fromfile(cls, data, file_path):
|
||||||
|
kwargs = super().fromfile(data, file_path)
|
||||||
|
|
||||||
|
if 'can_search' not in data:
|
||||||
|
raise ValueError('Missing can_search')
|
||||||
|
can_search = data['can_search']
|
||||||
|
if not isinstance(can_search, bool):
|
||||||
|
raise ValueError('can_search has to be boolean!')
|
||||||
|
kwargs['can_search'] = can_search
|
||||||
|
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def tofile(self):
|
||||||
|
result = super().tofile()
|
||||||
|
result['can_search'] = self.can_search
|
||||||
|
return result
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
|
||||||
class AreaLocation(LocationModelMixin, GeometryMapItemWithLevel):
|
class AreaLocation(LocationModelMixin, GeometryMapItemWithLevel):
|
||||||
|
LOCATION_TYPES = (
|
||||||
|
('level', _('Level')),
|
||||||
|
('area', _('General Area')),
|
||||||
|
('room', _('Room')),
|
||||||
|
('roomsegment', _('Room Segment')),
|
||||||
|
('poi', _('Point of Interest')),
|
||||||
|
)
|
||||||
|
LOCATION_TYPES_ORDER = tuple(name for name, title in LOCATION_TYPES)
|
||||||
|
|
||||||
|
location_type = models.CharField(max_length=20, verbose_name=_('Location Type'), choices=LOCATION_TYPES)
|
||||||
titles = JSONField()
|
titles = JSONField()
|
||||||
|
can_search = models.BooleanField(default=True, verbose_name=_('can be searched'))
|
||||||
|
can_describe = models.BooleanField(default=True, verbose_name=_('can be used to describe a position'))
|
||||||
groups = models.ManyToManyField(LocationGroup, verbose_name=_('Location Groups'), blank=True)
|
groups = models.ManyToManyField(LocationGroup, verbose_name=_('Location Groups'), blank=True)
|
||||||
|
|
||||||
geomtype = 'polygon'
|
geomtype = 'polygon'
|
||||||
|
@ -93,6 +129,45 @@ class AreaLocation(LocationModelMixin, GeometryMapItemWithLevel):
|
||||||
def location_id(self):
|
def location_id(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
def get_in_areas(self):
|
||||||
|
last_update = get_last_mapdata_update()
|
||||||
|
if last_update is None:
|
||||||
|
return self._get_in_areas()
|
||||||
|
|
||||||
|
cache_key = 'c3nav__mapdata__location__in__areas__'+last_update.isoformat()+'__'+self.name,
|
||||||
|
in_areas = cache.get(cache_key)
|
||||||
|
if not in_areas:
|
||||||
|
in_areas = self._get_in_areas()
|
||||||
|
cache.set(cache_key, in_areas, 900)
|
||||||
|
|
||||||
|
return in_areas
|
||||||
|
|
||||||
|
def _get_in_areas(self):
|
||||||
|
my_area = self.geometry.area
|
||||||
|
|
||||||
|
in_areas = []
|
||||||
|
area_location_i = self.get_sort_key(self)
|
||||||
|
for location_type in reversed(self.LOCATION_TYPES_ORDER[:area_location_i]):
|
||||||
|
for arealocation in AreaLocation.objects.filter(location_type=location_type, level=self.level):
|
||||||
|
intersection_area = arealocation.geometry.intersection(self.geometry).area
|
||||||
|
if intersection_area and my_area / intersection_area > 0.99:
|
||||||
|
in_areas.append(arealocation)
|
||||||
|
|
||||||
|
return in_areas
|
||||||
|
|
||||||
|
@property
|
||||||
|
def subtitle(self):
|
||||||
|
return self.get_subtitle()
|
||||||
|
|
||||||
|
def get_subtitle(self):
|
||||||
|
items = [self.get_location_type_display()]
|
||||||
|
items += [area.title for area in self.get_in_areas()]
|
||||||
|
return ', '.join(items)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_sort_key(cls, arealocation):
|
||||||
|
return cls.LOCATION_TYPES_ORDER.index(arealocation.location_type)
|
||||||
|
|
||||||
@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)
|
||||||
|
@ -102,6 +177,27 @@ class AreaLocation(LocationModelMixin, GeometryMapItemWithLevel):
|
||||||
raise TypeError('groups has to be a list')
|
raise TypeError('groups has to be a list')
|
||||||
kwargs['groups'] = groups
|
kwargs['groups'] = groups
|
||||||
|
|
||||||
|
if 'location_type' not in data:
|
||||||
|
raise ValueError('Missing location type')
|
||||||
|
location_type = data['location_type']
|
||||||
|
if location_type not in dict(cls.LOCATION_TYPES):
|
||||||
|
raise ValueError('Invalid location type')
|
||||||
|
kwargs['location_tyoe'] = location_type
|
||||||
|
|
||||||
|
if 'can_search' not in data:
|
||||||
|
raise ValueError('Missing can_search')
|
||||||
|
can_search = data['can_search']
|
||||||
|
if not isinstance(can_search, bool):
|
||||||
|
raise ValueError('can_search has to be boolean!')
|
||||||
|
kwargs['can_search'] = can_search
|
||||||
|
|
||||||
|
if 'can_describe' not in data:
|
||||||
|
raise ValueError('Missing can_describe')
|
||||||
|
can_describe = data['can_describe']
|
||||||
|
if not isinstance(can_describe, bool):
|
||||||
|
raise ValueError('can_describe has to be boolean!')
|
||||||
|
kwargs['can_describe'] = can_describe
|
||||||
|
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_geojson_properties(self):
|
def get_geojson_properties(self):
|
||||||
|
@ -112,6 +208,9 @@ class AreaLocation(LocationModelMixin, GeometryMapItemWithLevel):
|
||||||
def tofile(self):
|
def tofile(self):
|
||||||
result = super().tofile()
|
result = super().tofile()
|
||||||
result['groups'] = sorted(self.groups.all().order_by('name').values_list('name', flat=True))
|
result['groups'] = sorted(self.groups.all().order_by('name').values_list('name', flat=True))
|
||||||
|
result['location_type'] = self.location_type
|
||||||
|
result['can_search'] = self.can_search
|
||||||
|
result['can_describe'] = self.can_describe
|
||||||
result.move_to_end('geometry')
|
result.move_to_end('geometry')
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue