nicer overlay point markers and optional clustering

This commit is contained in:
Gwendolyn 2024-12-26 04:08:03 +01:00
parent b596bc9c12
commit aea7df1b24
6 changed files with 86 additions and 41 deletions

View file

@ -396,7 +396,7 @@ def create_editor_form(editor_model):
'color_background', 'color_wall_fill', 'color_wall_border', 'color_door_fill', 'color_ground_fill',
'color_obstacles_default_fill', 'color_obstacles_default_border', 'stroke_color', 'stroke_width',
'stroke_opacity', 'fill_color', 'fill_opacity', 'interactive', 'point_icon', 'extra_data', 'show_label',
'show_geometry', 'show_label', 'show_geometry', 'default_geomtype',
'show_geometry', 'show_label', 'show_geometry', 'default_geomtype', 'cluster_points',
"load_group_display", "load_group_contribute",
"altitude_quest",
]

View file

@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2024-12-26 02:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mapdata', '0128_space_identifyable'),
]
operations = [
migrations.AddField(
model_name='dataoverlay',
name='cluster_points',
field=models.BooleanField(default=False, verbose_name='cluster points together when zoomed out'),
),
]

View file

@ -26,6 +26,8 @@ class DataOverlay(TitledMixin, AccessRestrictionMixin, models.Model):
fill_color = models.CharField(max_length=255, blank=True, null=True, verbose_name=_('default fill color'))
fill_opacity = models.FloatField(blank=True, null=True, verbose_name=_('fill opacity'))
cluster_points = models.BooleanField(default=False, verbose_name=_('cluster points together when zoomed out'))
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'))

View file

@ -375,6 +375,7 @@ class DataOverlaySchema(TitledSchema, DjangoModelSchema):
stroke_opacity: Optional[float]
fill_color: Optional[str]
fill_opacity: Optional[float]
cluster_points: bool

View file

@ -1880,18 +1880,6 @@ blink {
}
}
.overlay-point-icon {
> span {
display: inline-block;
width: 24px;
height: 24px;
line-height: 24px;
text-align: center;
font-size: 24px;
font-family: 'Material Symbols Outlined';
}
}
.data-overlay-popup {
.leaflet-popup-content {
margin: 0;
@ -1944,7 +1932,8 @@ blink {
}
}
.quest-icon {
.symbol-icon {
--icon-color: var(--color-primary);
> span {
display: inline-block;
width: 30px;
@ -1954,15 +1943,33 @@ blink {
font-size: 22px;
font-family: 'Material Symbols Outlined';
background-color: white;
color: var(--color-primary);
color: var(--icon-color);
border-radius: 100%;
box-shadow: 0 0 0 5px color-mix(in srgb, transparent, var(--color-primary) 60%);
box-shadow: 0 0 0 5px color-mix(in srgb, transparent, var(--icon-color) 60%);
transition: color, background-color 150ms ease-in-out;
&:hover {
background-color: var(--color-primary);
color: white;
cursor: default;
}
&.symbol-icon-interactive {
> span {
cursor: pointer;
&:hover {
background-color: var(--icon-color);
color: white;
}
}
}
&.symbol-icon-empty {
> span {
width: 14px;
height: 14px;
line-height: 14px;
font-size: 10px;
}
}
}

View file

@ -2775,7 +2775,7 @@ QuestsControl = ExpandingControl.extend({
pointToLayer: (geom, latlng) => {
return L.marker(latlng, {
icon: L.divIcon({
className: 'quest-icon',
className: 'symbol-icon symbol-icon-interactive',
html: `<span>${quest_icon}</span>`,
iconSize: [24, 24],
iconAnchor: [12, 12],
@ -3130,6 +3130,7 @@ class DataOverlay {
this.id = options.id;
this.title = options.title;
this.group = options.group;
this.cluster_points = options.cluster_points;
this.default_stroke_color = options.stroke_color;
this.default_stroke_width = options.stroke_width;
this.default_stroke_opacity = options.stroke_opacity;
@ -3144,7 +3145,11 @@ class DataOverlay {
for (const feature of features) {
const level_id = feature.level_id;
if (!(level_id in levels)) {
levels[level_id] = L.layerGroup([]);
if (this.cluster_points) {
levels[level_id] = L.markerClusterGroup();
} else {
levels[level_id] = L.layerGroup();
}
}
const style = {
'color': feature.stroke_color ?? this.default_stroke_color ?? 'var(--color-map-overlay)',
@ -3157,12 +3162,21 @@ class DataOverlay {
style,
interactive: feature.interactive,
pointToLayer: (geom, latlng) => {
return L.marker(latlng, {
title: feature.title,
icon: L.divIcon({
className: 'symbol-icon ' + (feature.point_icon ? '' : 'symbol-icon-empty ') + (feature.interactive ? 'symbol-icon-interactive' : ''),
html: `<span style="--icon-color: ${style.color}">${feature.point_icon ?? ''}</span>`,
iconSize: [24, 24],
iconAnchor: [12, 12],
})
});
if (feature.point_icon !== null) {
return L.marker(latlng, {
title: feature.title,
icon: L.divIcon({
className: 'overlay-point-icon',
html: `<span style="color: ${style.color}">${feature.point_icon}</span>`,
className: 'symbol-icon ' + (feature.interactive ? 'symbol-icon-interactive' : ''),
html: `<span style="--icon-color: ${style.color}">${feature.point_icon}</span>`,
iconSize: [24, 24],
iconAnchor: [12, 12],
})
@ -3173,27 +3187,30 @@ class DataOverlay {
...style
});
}
},
onEachFeature: (f, layer) => {
if (feature.interactive) {
layer.bindPopup(() => {
let html = `<h4>${feature.title}</h4>`;
if (feature.external_url != null) {
html += `<a href="${feature.external_url}" target="_blank">open external link</a>`;
}
if (feature.extra_data != null) {
html += '<table>';
for (const key in feature.extra_data) {
html += `<tr><th>${key}</th><td>${feature.extra_data[key]}</td></tr>`;
}
html += '</table>';
}
return html;
}, {
className: 'data-overlay-popup'
});
}
}
});
if (feature.interactive) {
layer.bindPopup(() => {
let html = `<h4>${feature.title}</h4>`;
if (feature.external_url != null) {
html += `<a href="${feature.external_url}" target="_blank">open external link</a>`;
}
if (feature.extra_data != null) {
html += '<table>';
for (const key in feature.extra_data) {
html += `<tr><th>${key}</th><td>${feature.extra_data[key]}</td></tr>`;
}
html += '</table>';
}
return html;
}, {
className: 'data-overlay-popup'
});
}
levels[level_id].addLayer(layer);
}