implement label settings
This commit is contained in:
parent
3ce5e663c5
commit
ecd7cc5637
8 changed files with 180 additions and 33 deletions
|
@ -3,6 +3,7 @@ import operator
|
|||
import os
|
||||
from functools import reduce
|
||||
from itertools import chain
|
||||
from operator import attrgetter
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
|
@ -124,8 +125,18 @@ class EditorFormBase(I18nModelFormMixin, ModelForm):
|
|||
help_text=category.help_text)
|
||||
self.fields[name] = field
|
||||
|
||||
if 'label_settings' in self.fields:
|
||||
self.fields.move_to_end('label_settings')
|
||||
|
||||
for field in tuple(self.fields.keys()):
|
||||
if field.startswith('label_override'):
|
||||
self.fields.move_to_end(field)
|
||||
|
||||
if 'category' in self.fields:
|
||||
self.fields['category'].label_from_instance = lambda obj: obj.title
|
||||
self.fields['category'].label_from_instance = attrgetter('title')
|
||||
|
||||
if 'label_settings' in self.fields:
|
||||
self.fields['label_settings'].label_from_instance = attrgetter('title')
|
||||
|
||||
if 'access_restriction' in self.fields:
|
||||
AccessRestriction = self.request.changeset.wrap_model('AccessRestriction')
|
||||
|
@ -268,12 +279,12 @@ class EditorFormBase(I18nModelFormMixin, ModelForm):
|
|||
def create_editor_form(editor_model):
|
||||
possible_fields = ['slug', 'name', 'title', 'title_plural', 'help_text', 'icon', 'join_edges', 'up_separate',
|
||||
'walk', 'ordering', 'category', 'width', 'groups', 'color', 'priority', 'hierarchy', 'icon_name',
|
||||
'show_labels', 'show_label',
|
||||
'base_altitude', 'waytype', 'access_restriction', 'height', 'default_height', 'door_height',
|
||||
'outside', 'can_search', 'can_describe', 'geometry', 'single', 'altitude', 'short_label',
|
||||
'origin_space', 'target_space', 'data', 'comment', 'slow_down_factor',
|
||||
'extra_seconds', 'speed', 'description', 'speed_up', 'description_up', 'enter_description',
|
||||
'level_change_description', 'base_mapdata_accessible',
|
||||
'label_settings', 'label_override', 'min_zoom', 'max_zoom', 'font_size',
|
||||
'allow_levels', 'allow_spaces', 'allow_areas', 'allow_pois', 'left', 'top', 'right', 'bottom']
|
||||
field_names = [field.name for field in editor_model._meta.get_fields() if not field.one_to_many]
|
||||
existing_fields = [name for name in possible_fields if name in field_names]
|
||||
|
|
|
@ -57,6 +57,7 @@ urlpatterns.extend(add_editor_urls('WayType'))
|
|||
urlpatterns.extend(add_editor_urls('AccessRestriction'))
|
||||
urlpatterns.extend(add_editor_urls('AccessRestrictionGroup'))
|
||||
urlpatterns.extend(add_editor_urls('Source'))
|
||||
urlpatterns.extend(add_editor_urls('LabelSettings'))
|
||||
urlpatterns.extend(add_editor_urls('Building', 'Level'))
|
||||
urlpatterns.extend(add_editor_urls('Space', 'Level', explicit_edit=True))
|
||||
urlpatterns.extend(add_editor_urls('Door', 'Level'))
|
||||
|
|
|
@ -55,6 +55,7 @@ def main_index(request):
|
|||
child_model(request, 'WayType'),
|
||||
child_model(request, 'AccessRestriction'),
|
||||
child_model(request, 'AccessRestrictionGroup'),
|
||||
child_model(request, 'LabelSettings'),
|
||||
child_model(request, 'Source'),
|
||||
],
|
||||
}, fields=('can_create_level', 'child_models'))
|
||||
|
|
97
src/c3nav/mapdata/migrations/0075_label_settings.py
Normal file
97
src/c3nav/mapdata/migrations/0075_label_settings.py
Normal file
|
@ -0,0 +1,97 @@
|
|||
# Generated by Django 2.2.8 on 2019-12-21 23:27
|
||||
|
||||
import c3nav.mapdata.fields
|
||||
from decimal import Decimal
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mapdata', '0074_show_labels'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='LabelSettings',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', c3nav.mapdata.fields.I18nField(fallback_any=True, plural_name='titles', verbose_name='Title')),
|
||||
('min_zoom', models.DecimalField(decimal_places=1, default=-10, max_digits=3, validators=[django.core.validators.MinValueValidator(Decimal('-10')), django.core.validators.MaxValueValidator(Decimal('10'))], verbose_name='min zoom')),
|
||||
('max_zoom', models.DecimalField(decimal_places=1, default=10, max_digits=3, validators=[django.core.validators.MinValueValidator(Decimal('-10')), django.core.validators.MaxValueValidator(Decimal('10'))], verbose_name='max zoom')),
|
||||
('font_size', models.IntegerField(default=12, validators=[django.core.validators.MinValueValidator(12), django.core.validators.MaxValueValidator(30)], verbose_name='font size')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Label Settings',
|
||||
'verbose_name_plural': 'Label Settings',
|
||||
'default_related_name': 'labelsettings',
|
||||
},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='area',
|
||||
name='show_label',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='level',
|
||||
name='show_label',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='locationgroup',
|
||||
name='show_labels',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='poi',
|
||||
name='show_label',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='space',
|
||||
name='show_label',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='area',
|
||||
name='label_override',
|
||||
field=c3nav.mapdata.fields.I18nField(blank=True, fallback_any=True, plural_name='label_overrides', verbose_name='Label override'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='level',
|
||||
name='label_override',
|
||||
field=c3nav.mapdata.fields.I18nField(blank=True, fallback_any=True, plural_name='label_overrides', verbose_name='Label override'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poi',
|
||||
name='label_override',
|
||||
field=c3nav.mapdata.fields.I18nField(blank=True, fallback_any=True, plural_name='label_overrides', verbose_name='Label override'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='space',
|
||||
name='label_override',
|
||||
field=c3nav.mapdata.fields.I18nField(blank=True, fallback_any=True, plural_name='label_overrides', verbose_name='Label override'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='area',
|
||||
name='label_settings',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='areas', to='mapdata.LabelSettings', verbose_name='label settings'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='level',
|
||||
name='label_settings',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='levels', to='mapdata.LabelSettings', verbose_name='label settings'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='locationgroup',
|
||||
name='label_settings',
|
||||
field=models.ForeignKey(help_text='unless location specifies otherwise', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='locationgroups', to='mapdata.LabelSettings', verbose_name='label settings'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='poi',
|
||||
name='label_settings',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='pois', to='mapdata.LabelSettings', verbose_name='label settings'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='space',
|
||||
name='label_settings',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='spaces', to='mapdata.LabelSettings', verbose_name='label settings'),
|
||||
),
|
||||
]
|
|
@ -1,7 +1,8 @@
|
|||
from contextlib import suppress
|
||||
from decimal import Decimal
|
||||
from operator import attrgetter
|
||||
|
||||
from django.core.validators import RegexValidator
|
||||
from django.core.validators import RegexValidator, MinValueValidator, MaxValueValidator
|
||||
from django.db import models
|
||||
from django.db.models import FieldDoesNotExist, Prefetch
|
||||
from django.urls import reverse
|
||||
|
@ -103,7 +104,7 @@ class Location(LocationSlug, AccessRestrictionMixin, TitledMixin, models.Model):
|
|||
result = super().serialize(detailed=detailed, **kwargs)
|
||||
if not detailed:
|
||||
fields = ('id', 'type', 'slug', 'title', 'subtitle', 'icon', 'point', 'bounds', 'grid_square',
|
||||
'locations', 'on_top_of', 'show_label')
|
||||
'locations', 'on_top_of', 'label_settings', 'label_override')
|
||||
result = {name: result[name] for name in fields if name in result}
|
||||
return result
|
||||
|
||||
|
@ -158,14 +159,10 @@ class Location(LocationSlug, AccessRestrictionMixin, TitledMixin, models.Model):
|
|||
|
||||
|
||||
class SpecificLocation(Location, models.Model):
|
||||
SHOW_LABEL_OPTIONS = (
|
||||
('inherit', _('inherit from groups (default)')),
|
||||
('show_text', _('yes, show the title')),
|
||||
('no', _('don\'t show')),
|
||||
)
|
||||
|
||||
groups = models.ManyToManyField('mapdata.LocationGroup', verbose_name=_('Location Groups'), blank=True)
|
||||
show_label = models.CharField(_('show label'), max_length=16, default='inherit', choices=SHOW_LABEL_OPTIONS)
|
||||
label_settings = models.ForeignKey('mapdata.LabelSettings', null=True, on_delete=models.PROTECT,
|
||||
verbose_name=_('label settings'))
|
||||
label_override = I18nField(_('Label override'), plural_name='label_overrides', blank=True, fallback_any=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
@ -184,18 +181,22 @@ class SpecificLocation(Location, models.Model):
|
|||
for category, items in groups.items()
|
||||
if getattr(category, 'allow_'+self.__class__._meta.default_related_name)}
|
||||
result['groups'] = groups
|
||||
result['show_label'] = self.get_show_label()
|
||||
|
||||
label_settings = self.get_label_settings()
|
||||
if label_settings:
|
||||
result['label_settings'] = label_settings.serialize(detailed=False)
|
||||
if self.label_overrides:
|
||||
# todo: what if only one language is set?
|
||||
result['label_override'] = self.label_override
|
||||
return result
|
||||
|
||||
def get_show_label(self):
|
||||
if self.show_label == 'inherit':
|
||||
for group in self.groups.all():
|
||||
if group.show_labels != 'no':
|
||||
return group.show_labels
|
||||
return None
|
||||
if self.show_label == 'no':
|
||||
return None
|
||||
return self.show_label
|
||||
def get_label_settings(self):
|
||||
if self.label_settings:
|
||||
return self.label_settings
|
||||
for group in self.groups.all():
|
||||
if group.label_settings:
|
||||
return group.label_settings
|
||||
return None
|
||||
|
||||
def details_display(self, **kwargs):
|
||||
result = super().details_display(**kwargs)
|
||||
|
@ -304,17 +305,13 @@ class LocationGroupManager(models.Manager):
|
|||
|
||||
|
||||
class LocationGroup(Location, models.Model):
|
||||
SHOW_LABELS_OPTIONS = (
|
||||
('no', _('no (default)')),
|
||||
('show_text', _('yes, show the title')),
|
||||
)
|
||||
|
||||
category = models.ForeignKey(LocationGroupCategory, related_name='groups', on_delete=models.PROTECT,
|
||||
verbose_name=_('Category'))
|
||||
priority = models.IntegerField(default=0, db_index=True)
|
||||
hierarchy = models.IntegerField(default=0, db_index=True, verbose_name=_('hierarchy'))
|
||||
show_labels = models.CharField(_('show labels'), max_length=16, default='no', choices=SHOW_LABELS_OPTIONS,
|
||||
help_text=_('unless location specifies otherwise'))
|
||||
label_settings = models.ForeignKey('mapdata.LabelSettings', null=True, on_delete=models.PROTECT,
|
||||
verbose_name=_('label settings'),
|
||||
help_text=_('unless location specifies otherwise'))
|
||||
color = models.CharField(null=True, blank=True, max_length=32, verbose_name=_('background color'))
|
||||
|
||||
objects = LocationGroupManager()
|
||||
|
@ -418,3 +415,32 @@ class LocationRedirect(LocationSlug):
|
|||
|
||||
class Meta:
|
||||
default_related_name = 'redirect'
|
||||
|
||||
|
||||
class LabelSettings(SerializableMixin, models.Model):
|
||||
title = I18nField(_('Title'), plural_name='titles', fallback_any=True)
|
||||
min_zoom = models.DecimalField(_('min zoom'), max_digits=3, decimal_places=1, default=-10,
|
||||
validators=[MinValueValidator(Decimal('-10')),
|
||||
MaxValueValidator(Decimal('10'))])
|
||||
max_zoom = models.DecimalField(_('max zoom'), max_digits=3, decimal_places=1, default=10,
|
||||
validators=[MinValueValidator(Decimal('-10')),
|
||||
MaxValueValidator(Decimal('10'))])
|
||||
font_size = models.IntegerField(_('font size'), default=12,
|
||||
validators=[MinValueValidator(12),
|
||||
MaxValueValidator(30)])
|
||||
|
||||
def _serialize(self, detailed=True, **kwargs):
|
||||
result = super()._serialize(detailed=detailed, **kwargs)
|
||||
if detailed:
|
||||
result['titles'] = self.titles
|
||||
if self.min_zoom > -10:
|
||||
result['min_zoom'] = self.min_zoom
|
||||
if self.max_zoom < 10:
|
||||
result['max_zoom'] = self.max_zoom
|
||||
result['font_size'] = self.font_size
|
||||
return result
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Label Settings')
|
||||
verbose_name_plural = _('Label Settings')
|
||||
default_related_name = 'labelsettings'
|
||||
|
|
|
@ -78,6 +78,8 @@ def round_polygon(coordinates):
|
|||
# round each ring on it's own and remove rings that are invalid
|
||||
# if the exterior ring is invalid, return and empty polygon
|
||||
coordinates = tuple(round_coordinates(ring) for ring in coordinates)
|
||||
if not coordinates:
|
||||
return coordinates
|
||||
exterior, *interiors = coordinates
|
||||
if not check_ring(exterior):
|
||||
return ()
|
||||
|
|
|
@ -579,6 +579,10 @@ main.show-options #resultswrapper #route-options {
|
|||
white-space: nowrap;
|
||||
}
|
||||
.location-label-text {
|
||||
background-color: rgba(255, 255, 255, 0.6);
|
||||
line-height: 100%;
|
||||
padding: 2px 3px;
|
||||
border-radius: 2px;
|
||||
white-space: nowrap;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ c3nav = {
|
|||
location.match = ' ' + location.title_words.join(' ') + ' ' + location.subtitle_words.join(' ') + ' ' + location.slug;
|
||||
locations.push(location);
|
||||
locations_by_id[location.id] = location;
|
||||
if (location.point && location.show_label) {
|
||||
if (location.point && location.label_settings) {
|
||||
location.label = c3nav._build_location_label(location);
|
||||
if (!(location.point[0] in labels)) labels[location.point[0]] = [];
|
||||
labels[location.point[0]].push(location);
|
||||
|
@ -303,12 +303,15 @@ c3nav = {
|
|||
update_location_labels: function() {
|
||||
c3nav._labelLayer.clearLayers();
|
||||
var labels = c3nav.labels[c3nav._levelControl.currentLevel],
|
||||
bounds = c3nav.map.getBounds();
|
||||
bounds = c3nav.map.getBounds(),
|
||||
zoom = c3nav.map.getZoom();
|
||||
if (!labels) return;
|
||||
|
||||
for (var location of labels) {
|
||||
if (bounds.contains(location.label.getLatLng())) {
|
||||
c3nav._labelLayer._maybeAddLayerToRBush(location.label);
|
||||
if (bounds.contains(location.label.getLatLng()) &&
|
||||
(location.label_settings.min_zoom || -10) < zoom &&
|
||||
(location.label_settings.max_zoom || 10) > zoom) {
|
||||
c3nav._labelLayer._maybeAddLayerToRBush(location.label);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -801,9 +804,11 @@ c3nav = {
|
|||
return html[0].outerHTML;
|
||||
},
|
||||
_build_location_label: function(location) {
|
||||
var html = $('<div class="location-label-text">').text(location.label_override || location.title);
|
||||
html.css('font-size', location.label_settings.font_size+'px');
|
||||
return L.marker(L.GeoJSON.coordsToLatLng(location.point.slice(1)), {
|
||||
icon: L.divIcon({
|
||||
html: $('<div class="location-label-text">').text(location.title)[0].outerHTML,
|
||||
html: html[0].outerHTML,
|
||||
iconSize: null,
|
||||
className: 'location-label'
|
||||
}),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue