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_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', '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', '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", "load_group_display", "load_group_contribute",
"altitude_quest", "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_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')) 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')) 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_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] stroke_opacity: Optional[float]
fill_color: Optional[str] fill_color: Optional[str]
fill_opacity: Optional[float] 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 { .data-overlay-popup {
.leaflet-popup-content { .leaflet-popup-content {
margin: 0; margin: 0;
@ -1944,7 +1932,8 @@ blink {
} }
} }
.quest-icon { .symbol-icon {
--icon-color: var(--color-primary);
> span { > span {
display: inline-block; display: inline-block;
width: 30px; width: 30px;
@ -1954,17 +1943,35 @@ blink {
font-size: 22px; font-size: 22px;
font-family: 'Material Symbols Outlined'; font-family: 'Material Symbols Outlined';
background-color: white; background-color: white;
color: var(--color-primary); color: var(--icon-color);
border-radius: 100%; 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; transition: color, background-color 150ms ease-in-out;
cursor: default;
}
&.symbol-icon-interactive {
> span {
cursor: pointer;
&:hover { &:hover {
background-color: var(--color-primary); background-color: var(--icon-color);
color: white; color: white;
} }
} }
}
&.symbol-icon-empty {
> span {
width: 14px;
height: 14px;
line-height: 14px;
font-size: 10px;
}
}
} }
.marker-cluster { .marker-cluster {

View file

@ -2775,7 +2775,7 @@ QuestsControl = ExpandingControl.extend({
pointToLayer: (geom, latlng) => { pointToLayer: (geom, latlng) => {
return L.marker(latlng, { return L.marker(latlng, {
icon: L.divIcon({ icon: L.divIcon({
className: 'quest-icon', className: 'symbol-icon symbol-icon-interactive',
html: `<span>${quest_icon}</span>`, html: `<span>${quest_icon}</span>`,
iconSize: [24, 24], iconSize: [24, 24],
iconAnchor: [12, 12], iconAnchor: [12, 12],
@ -3130,6 +3130,7 @@ class DataOverlay {
this.id = options.id; this.id = options.id;
this.title = options.title; this.title = options.title;
this.group = options.group; this.group = options.group;
this.cluster_points = options.cluster_points;
this.default_stroke_color = options.stroke_color; this.default_stroke_color = options.stroke_color;
this.default_stroke_width = options.stroke_width; this.default_stroke_width = options.stroke_width;
this.default_stroke_opacity = options.stroke_opacity; this.default_stroke_opacity = options.stroke_opacity;
@ -3144,7 +3145,11 @@ class DataOverlay {
for (const feature of features) { for (const feature of features) {
const level_id = feature.level_id; const level_id = feature.level_id;
if (!(level_id in levels)) { 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 = { const style = {
'color': feature.stroke_color ?? this.default_stroke_color ?? 'var(--color-map-overlay)', 'color': feature.stroke_color ?? this.default_stroke_color ?? 'var(--color-map-overlay)',
@ -3157,12 +3162,21 @@ class DataOverlay {
style, style,
interactive: feature.interactive, interactive: feature.interactive,
pointToLayer: (geom, latlng) => { 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) { if (feature.point_icon !== null) {
return L.marker(latlng, { return L.marker(latlng, {
title: feature.title, title: feature.title,
icon: L.divIcon({ icon: L.divIcon({
className: 'overlay-point-icon', className: 'symbol-icon ' + (feature.interactive ? 'symbol-icon-interactive' : ''),
html: `<span style="color: ${style.color}">${feature.point_icon}</span>`, html: `<span style="--icon-color: ${style.color}">${feature.point_icon}</span>`,
iconSize: [24, 24], iconSize: [24, 24],
iconAnchor: [12, 12], iconAnchor: [12, 12],
}) })
@ -3173,8 +3187,8 @@ class DataOverlay {
...style ...style
}); });
} }
} },
}); onEachFeature: (f, layer) => {
if (feature.interactive) { if (feature.interactive) {
layer.bindPopup(() => { layer.bindPopup(() => {
let html = `<h4>${feature.title}</h4>`; let html = `<h4>${feature.title}</h4>`;
@ -3194,6 +3208,9 @@ class DataOverlay {
className: 'data-overlay-popup' className: 'data-overlay-popup'
}); });
} }
}
});
levels[level_id].addLayer(layer); levels[level_id].addLayer(layer);
} }