overlays
default geomtype, permission fix, name fix, remove ungrouped header, fix id filter
This commit is contained in:
parent
13cf207ee6
commit
934aa60be4
10 changed files with 96 additions and 41 deletions
|
@ -422,6 +422,7 @@ def create_editor_form(editor_model):
|
|||
'color_ground_fill', 'color_obstacles_default_fill', 'color_obstacles_default_border',
|
||||
'stroke_color', 'stroke_width', 'fill_color', 'interactive', 'point_icon', 'extra_data',
|
||||
'show_label', 'show_geometry', 'external_url',
|
||||
'show_label', 'show_geometry', 'external_url', 'default_geomtype',
|
||||
]
|
||||
field_names = [field.name for field in editor_model._meta.get_fields()
|
||||
if not field.one_to_many and not isinstance(field, ManyToManyRel)]
|
||||
|
|
|
@ -1370,6 +1370,7 @@ editor = {
|
|||
}
|
||||
form.addClass('creation-lock');
|
||||
const geomtypes = form.attr('data-geomtype').split(',');
|
||||
const default_geomtype = form.attr('data-default-geomtype');
|
||||
|
||||
const startGeomEditing = (geomtype) => {
|
||||
editor._creating_type = geomtype;
|
||||
|
@ -1387,6 +1388,8 @@ editor = {
|
|||
}
|
||||
}
|
||||
|
||||
let selected_geomtype = geomtypes[0];
|
||||
|
||||
if (geomtypes.length > 1) {
|
||||
const selector = $('<select id="geomtype-selector"></select>');
|
||||
const geomtypeNames = {
|
||||
|
@ -1395,13 +1398,18 @@ editor = {
|
|||
point: 'Point'
|
||||
}; // TODO: translations
|
||||
for(const geomtype of geomtypes) {
|
||||
selector.append(`<option value="${geomtype}">${geomtypeNames[geomtype]}</option>`);
|
||||
const option = $(`<option value="${geomtype}">${geomtypeNames[geomtype]}</option>`);
|
||||
if (geomtype === default_geomtype) {
|
||||
option.attr('selected', true);
|
||||
selected_geomtype = geomtype;
|
||||
}
|
||||
selector.append(option);
|
||||
}
|
||||
|
||||
selector.on('change', e => startGeomEditing(e.target.value));
|
||||
form.prepend(selector);
|
||||
}
|
||||
startGeomEditing(geomtypes[0]);
|
||||
startGeomEditing(selected_geomtype);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
{% endif %}
|
||||
</h3>
|
||||
{% bootstrap_messages %}
|
||||
<form action="{{ path }}" method="post" {% if nozoom %}data-nozoom {% endif %}data-onbeforeunload {% if new %}data-new="{{ model_name }}" data-geomtype="{{ geomtype }}"{% else %}data-editing="{{ model_name }}-{{ pk }}"{% endif %}{% if access_restriction_select %} data-access-restriction-select{% endif %}>
|
||||
<form action="{{ path }}" method="post" {% if nozoom %}data-nozoom {% endif %}data-onbeforeunload {% if new %}data-new="{{ model_name }}" data-geomtype="{{ geomtype }}" {% if default_geomtype %}data-default-geomtype="{{ default_geomtype }}{% endif %}"{% else %}data-editing="{{ model_name }}-{{ pk }}"{% endif %}{% if access_restriction_select %} data-access-restriction-select{% endif %}>
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
{% buttons %}
|
||||
|
|
|
@ -130,6 +130,7 @@ def overlay_feature_edit(request, level=None, overlay=None, pk=None):
|
|||
'title': obj.title if obj else None,
|
||||
'geometry_url': geometry_url,
|
||||
'geomtype': 'polygon,linestring,point',
|
||||
'default_geomtype': overlay.default_geomtype,
|
||||
}
|
||||
|
||||
space_id = None
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.1.3 on 2024-12-16 11:25
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mapdata', '0117_alter_dataoverlay_fill_color_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dataoverlay',
|
||||
name='default_geomtype',
|
||||
field=models.CharField(blank=True, choices=[('polygon', 'Polygon'), ('line', 'Line'), ('point', 'Point')], max_length=255, null=True, verbose_name='default geometry type'),
|
||||
),
|
||||
]
|
|
@ -13,10 +13,18 @@ from c3nav.mapdata.utils.json import format_geojson
|
|||
|
||||
|
||||
class DataOverlay(TitledMixin, AccessRestrictionMixin, models.Model):
|
||||
class GeometryType(models.TextChoices):
|
||||
POLYGON = "polygon", _("Polygon")
|
||||
LINESTRING = "linestring", _("Line string")
|
||||
POINT = "point", _("Point")
|
||||
|
||||
description = models.TextField(blank=True, verbose_name=_('Description'))
|
||||
stroke_color = models.CharField(max_length=255, blank=True, null=True, verbose_name=_('default stroke color'))
|
||||
stroke_width = models.FloatField(blank=True, null=True, verbose_name=_('default stroke width'))
|
||||
fill_color = models.CharField(max_length=255, blank=True, null=True, verbose_name=_('default fill color'))
|
||||
|
||||
default_geomtype = models.CharField(max_length=255, blank=True, null=True, choices=GeometryType, verbose_name=_('default geometry type'))
|
||||
|
||||
pull_url = models.URLField(blank=True, null=True, verbose_name=_('pull URL'))
|
||||
pull_headers: dict[str, str] = SchemaField(schema=dict[str, str], null=True,
|
||||
verbose_name=_('headers for pull http request (JSON object)'))
|
||||
|
|
|
@ -6,7 +6,8 @@ from pydantic import Field as APIField
|
|||
|
||||
from c3nav.api.exceptions import APIRequestValidationFailed
|
||||
from c3nav.api.schema import BaseSchema
|
||||
from c3nav.mapdata.models import Level, LocationGroup, LocationGroupCategory, MapUpdate, Space, Door, Building
|
||||
from c3nav.mapdata.models import Level, LocationGroup, LocationGroupCategory, MapUpdate, Space, Door, Building, \
|
||||
DataOverlay
|
||||
from c3nav.mapdata.models.access import AccessPermission
|
||||
|
||||
|
||||
|
@ -145,7 +146,7 @@ class ByOverlayFilter(FilterSchema):
|
|||
def validate(self, request):
|
||||
super().validate(request)
|
||||
if self.overlay is not None:
|
||||
assert_valid_value(request, Level, "pk", {self.overlay})
|
||||
assert_valid_value(request, DataOverlay, "pk", {self.overlay})
|
||||
|
||||
def filter_qs(self, request, qs: QuerySet) -> QuerySet:
|
||||
if self.overlay is not None:
|
||||
|
|
|
@ -6,6 +6,7 @@ from django.utils.translation import ngettext_lazy
|
|||
from c3nav.mapdata.models import DataOverlay
|
||||
from c3nav.mapdata.models.access import AccessPermission, AccessRestriction
|
||||
from c3nav.mapdata.models.locations import Position
|
||||
from c3nav.mapdata.schemas.models import DataOverlaySchema
|
||||
|
||||
|
||||
def get_user_data(request):
|
||||
|
@ -32,16 +33,13 @@ def get_user_data(request):
|
|||
if request.user.is_authenticated:
|
||||
result['title'] = request.user.username
|
||||
|
||||
# TODO: permissions for overlays
|
||||
|
||||
result.update({
|
||||
'overlays': [{
|
||||
'id': overlay.pk,
|
||||
'name': overlay.title,
|
||||
'group': None, # TODO
|
||||
'stroke_color': overlay.stroke_color,
|
||||
'stroke_width': overlay.stroke_width,
|
||||
'fill_color': overlay.fill_color,
|
||||
} for overlay in DataOverlay.objects.all()]
|
||||
'overlays': [
|
||||
DataOverlaySchema.model_validate(overlay).model_dump()
|
||||
for overlay
|
||||
in DataOverlay.qs_for_request(request)
|
||||
]
|
||||
})
|
||||
return result
|
||||
|
||||
|
|
|
@ -1811,14 +1811,7 @@ blink {
|
|||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
margin-left: 3ch;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
margin-right: 0.5rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1832,6 +1825,16 @@ blink {
|
|||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
margin-right: 0.5rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.leaflet-control-overlays-expanded > .content {
|
||||
|
|
|
@ -2615,6 +2615,7 @@ KeyControl = L.Control.extend({
|
|||
OverlayControl = L.Control.extend({
|
||||
options: {position: 'topright', addClasses: '', levels: {}},
|
||||
_overlays: {},
|
||||
_ungrouped: [],
|
||||
_groups: {},
|
||||
_initialActiveOverlays: null,
|
||||
_initialCollapsedGroups: null,
|
||||
|
@ -2685,14 +2686,19 @@ OverlayControl = L.Control.extend({
|
|||
|
||||
addOverlay: function (overlay) {
|
||||
this._overlays[overlay.id] = overlay;
|
||||
if (overlay.group in this._groups) {
|
||||
this._groups[overlay.group].overlays.push(overlay);
|
||||
if (overlay.group == null) {
|
||||
this._ungrouped.push(overlay);
|
||||
} else {
|
||||
this._groups[overlay.group] = {
|
||||
expanded: this._initialCollapsedGroups === null || !this._initialCollapsedGroups.includes(overlay.group),
|
||||
overlays: [overlay],
|
||||
};
|
||||
if (overlay.group in this._groups) {
|
||||
this._groups[overlay.group].overlays.push(overlay);
|
||||
} else {
|
||||
this._groups[overlay.group] = {
|
||||
expanded: this._initialCollapsedGroups === null || !this._initialCollapsedGroups.includes(overlay.group),
|
||||
overlays: [overlay],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
this.render();
|
||||
},
|
||||
|
||||
|
@ -2709,7 +2715,28 @@ OverlayControl = L.Control.extend({
|
|||
|
||||
render: function () {
|
||||
if (!this._content) return;
|
||||
|
||||
const ungrouped = document.createDocumentFragment();
|
||||
const groups = document.createDocumentFragment();
|
||||
|
||||
|
||||
const render_overlays = (overlays, container) => {
|
||||
for (const overlay of overlays) {
|
||||
const label = document.createElement('label');
|
||||
const checkbox = document.createElement('input');
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.dataset.id = overlay.id;
|
||||
if (overlay.visible) {
|
||||
checkbox.checked = true;
|
||||
}
|
||||
label.append(checkbox, overlay.title);
|
||||
container.append(label);
|
||||
}
|
||||
};
|
||||
|
||||
render_overlays(this._ungrouped, ungrouped);
|
||||
|
||||
|
||||
for (const group in this._groups) {
|
||||
const group_container = document.createElement('div');
|
||||
group_container.classList.add('overlay-group');
|
||||
|
@ -2721,20 +2748,10 @@ OverlayControl = L.Control.extend({
|
|||
const title = document.createElement('h4');
|
||||
title.innerText = group;
|
||||
group_container.append(title);
|
||||
for (const overlay of this._groups[group].overlays) {
|
||||
const label = document.createElement('label');
|
||||
const checkbox = document.createElement('input');
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.dataset.id = overlay.id;
|
||||
if (overlay.visible) {
|
||||
checkbox.checked = true;
|
||||
}
|
||||
label.append(checkbox, overlay.name);
|
||||
group_container.append(label);
|
||||
}
|
||||
render_overlays(this._groups[group].overlays, group_container);
|
||||
groups.append(group_container);
|
||||
}
|
||||
this._content.replaceChildren(...groups.children);
|
||||
this._content.replaceChildren(...ungrouped.children, ...groups.children);
|
||||
},
|
||||
|
||||
expand: function () {
|
||||
|
@ -2832,8 +2849,8 @@ class DataOverlay {
|
|||
|
||||
constructor(options) {
|
||||
this.id = options.id;
|
||||
this.name = options.name;
|
||||
this.group = options.group ?? 'ungrouped';
|
||||
this.title = options.title;
|
||||
this.group = options.group;
|
||||
this.default_stroke_color = options.stroke_color;
|
||||
this.default_stroke_width = options.stroke_width;
|
||||
this.default_fill_color = options.fill_color;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue