nicer overlay point markers and optional clustering
This commit is contained in:
parent
b596bc9c12
commit
aea7df1b24
6 changed files with 86 additions and 41 deletions
|
@ -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",
|
||||||
]
|
]
|
||||||
|
|
|
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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'))
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue