load indicators and some cleanup

This commit is contained in:
Gwendolyn 2024-12-25 19:05:21 +01:00
parent f434e154d0
commit cba970e058
2 changed files with 210 additions and 86 deletions

View file

@ -1920,4 +1920,29 @@ blink {
}
}
}
}
.location-load-info {
transform: translateX(-50%) translateY(150%);
border: 1px solid var(--color-overlay-background);
margin: 2px auto auto;
width: 40px;
height: 8px;
> .load-indicator {
--location-load-color: color-mix(in hsl, green, red var(--location-load-value));
display: block;
width: 100%;
height: 100%;
appearance: none;
border: 2px solid var(--location-load-color);
background: white;
&::after {
content: '';
display: block;
height: 100%;
width: var(--location-load-value);
background: var(--location-load-color);
}
}
}

View file

@ -104,6 +104,7 @@ c3nav = {
c3nav.load_material_symbols_if_needed();
c3nav.load_searchable_locations();
c3nav.load_load_indicator_data();
$('#messages').find('ul.messages li').each(function () {
$(this).prepend(
@ -145,12 +146,31 @@ c3nav = {
c3nav._searchable_locations_timer = null;
c3nav_api.get('map/locations?searchable=true')
.then(c3nav._searchable_locations_loaded)
.catch(() => {
.catch((err) => {
console.error(err);
if (c3nav._searchable_locations_timer === null) {
c3nav._searchable_locations_timer = window.setTimeout(c3nav.load_searchable_locations, c3nav.init_completed ? 300000 : 15000);
}
});
},
_load_indicator_timer: null,
load_load_indicator_data: function () {
c3nav._load_indicator_timer = null;
c3nav_api.get('map/load')
.then(data => {
if (data) {
c3nav._location_load_groups = data;
}
if (c3nav._load_indicator_timer === null) {
c3nav._load_indicator_timer = window.setTimeout(c3nav.load_load_indicator_data, c3nav._load_indicator_interval);
}
c3nav.update_load_data();
})
.catch(err => {
console.error(err);
});
},
_sort_labels: function (a, b) {
let result = (a[0].effective_label_settings.min_zoom || -10) - (b[0].effective_label_settings.min_zoom || -10);
if (result === 0) {
@ -160,12 +180,15 @@ c3nav = {
},
_last_time_searchable_locations_loaded: null,
_searchable_locations_interval: 120000,
_load_indicator_interval: 10000, // TODO: set to a sensible number
loadIndicatorLocations: [],
_searchable_locations_loaded: function (data) {
c3nav._last_time_searchable_locations_loaded = Date.now();
if (data !== undefined) {
const locations = [];
const locations_by_id = {};
const labels = {};
const loadIndicatorLocations = [];
for (let i = 0; i < data.length; i++) {
const location = data[i];
location.elem = c3nav._build_location_html(location);
@ -178,6 +201,15 @@ c3nav = {
if (!(location.point[0] in labels)) labels[location.point[0]] = [];
labels[location.point[0]].push([location, c3nav._build_location_label(location)]);
}
if (location.point && location.load_group_display) {
loadIndicatorLocations.push({
level: location.point[0],
coords: location.point.slice(1),
load_id: location.load_group_display,
});
}
}
for (const level_id in labels) {
labels[level_id].sort(c3nav._sort_labels);
@ -185,6 +217,8 @@ c3nav = {
c3nav.locations = locations;
c3nav.locations_by_id = locations_by_id;
c3nav.labels = labels;
c3nav.loadIndicatorLocations = loadIndicatorLocations;
c3nav.update_load_data();
} else {
// 304, nothing to do!
}
@ -195,6 +229,52 @@ c3nav = {
c3nav._searchable_locations_timer = window.setTimeout(c3nav.load_searchable_locations, c3nav._searchable_locations_interval);
}
},
_loadIndicatorLabels: {},
_location_load_groups: {},
update_load_data: function() {
c3nav._loadIndicatorLabels = {};
for (const location of c3nav.loadIndicatorLocations) {
const load = c3nav._location_load_groups[location.load_id];
if (typeof load != "number") continue;
const load_pct = Math.round(load * 100);
const html = $(`<div class="location-load-info"><div class="load-indicator" style="--location-load-value: ${load_pct}%;"></div>`);
const marker = L.marker(L.GeoJSON.coordsToLatLng(location.coords), {
icon: L.divIcon({
html: html[0].outerHTML,
iconSize: null,
className: ''
}),
interactive: false,
});
let levelLabels = c3nav._loadIndicatorLabels[location.level];
if (!levelLabels) {
levelLabels = c3nav._loadIndicatorLabels[location.level] = [];
}
levelLabels.push(marker);
}
c3nav._update_loadinfo_labels();
},
_update_loadinfo_labels: function () {
if (!c3nav._loadIndicatorLayer) return;
c3nav._loadIndicatorLayer.clearLayers();
if (!c3nav._loadIndicatorControl.enabled) return;
const labels = c3nav._loadIndicatorLabels[c3nav.current_level()] ?? [];
const bounds = c3nav.map.getBounds().pad(0.15);
if (!labels) return;
for (const label of labels) {
if (bounds.contains(label.getLatLng())) {
c3nav._loadIndicatorLayer._maybeAddLayerToRBush(label);
}
}
},
continue_init: function () {
c3nav.init_map();
@ -228,6 +308,7 @@ c3nav = {
c3nav.update_map_state(true);
}
c3nav.update_location_labels();
c3nav._update_loadinfo_labels();
}
c3nav.init_locationinputs();
@ -277,7 +358,8 @@ c3nav = {
window.setTimeout(c3nav.test_location, 1000);
c3nav._set_user_location(data.location);
})
.catch(() => {
.catch(err => {
console.error(err);
window.setTimeout(c3nav.test_location, 1000);
c3nav._set_user_location(null);
});
@ -398,7 +480,7 @@ c3nav = {
},
update_location_labels: function () {
if (!c3nav._labelControl.labelsActive) return;
if (!c3nav._labelControl.enabled) return;
c3nav._labelLayer.clearLayers();
const labels = c3nav.labels[c3nav.current_level()];
const bounds = c3nav.map.getBounds().pad(0.15);
@ -1040,6 +1122,8 @@ c3nav = {
for (let i = 0; i < new_text.length; i++) {
new_text[i] = new_text[i].trim();
}
const html = $('<div class="location-label-text">').append($('<span>').html('&#8239;' + new_text.join('&#8239;<br>&#8239;') + '&#8239;'));
html.css('font-size', location.effective_label_settings.font_size + 'px');
return L.marker(L.GeoJSON.coordsToLatLng(location.point.slice(1)), {
@ -1486,7 +1570,8 @@ c3nav = {
c3nav._questsLayers = {};
c3nav._firstRouteLevel = null;
c3nav._labelLayer = L.LayerGroup.collision({margin: 5}).addTo(c3nav.map);
for (i = c3nav.levels.length - 1; i >= 0; i--) {
c3nav._loadIndicatorLayer = L.LayerGroup.collision({margin: 5}).addTo(c3nav.map);
for (let i = c3nav.levels.length - 1; i >= 0; i--) {
const level = c3nav.levels[i];
const layerGroup = c3nav._levelControl.addLevel(level[0], level[2]);
c3nav._detailLayers[level[0]] = L.layerGroup().addTo(layerGroup);
@ -1499,12 +1584,48 @@ c3nav = {
c3nav._levelControl.finalize();
c3nav._levelControl.setLevel(c3nav.initial_level);
c3nav._labelControl = new LabelControl().addTo(c3nav.map);
c3nav._labelControl = new ToggleControl({
storageId: 'labels',
enabledIcon: c3nav._map_material_icon('label'),
disabledIcon: c3nav._map_material_icon('label_off'),
onEnable: () => {
c3nav._labelLayer.addTo(c3nav.map);
c3nav.update_location_labels();
},
onDisable: () => {
c3nav._labelLayer.clearLayers();
c3nav._labelLayer.remove();
},
}).addTo(c3nav.map);
c3nav._loadIndicatorControl = new ToggleControl({
storageId: 'load_indicator',
enabledIcon: c3nav._map_material_icon('bar_chart'),
disabledIcon: c3nav._map_material_icon('bar_chart_off'),
onEnable: () => {
c3nav._update_loadinfo_labels();
},
onDisable: () => {
c3nav._update_loadinfo_labels();
},
}).addTo(c3nav.map);
// setup grid control
if ($map.is('[data-grid]')) {
c3nav._gridLayer = new L.SquareGridLayer(JSON.parse($map.attr('data-grid')));
c3nav._gridControl = new SquareGridControl().addTo(c3nav.map);
c3nav._gridControl = new ToggleControl({
storageId: 'grid',
enabledIcon: c3nav._map_material_icon('grid_on'),
disabledIcon: c3nav._map_material_icon('grid_off'),
onEnable: () => {
console.log('grid enable');
c3nav._gridLayer.addTo(c3nav.map);
},
onDisable: () => {
console.log('grid disable');
c3nav._gridLayer.remove();
},
}).addTo(c3nav.map);
}
if (Object.values(c3nav.themes).length > 1) {
new ThemeControl().addTo(c3nav.map);
@ -1519,6 +1640,7 @@ c3nav = {
c3nav._update_overlays();
c3nav._update_quests();
c3nav._update_loadinfo_labels();
c3nav.map.on('click', c3nav._click_anywhere);
@ -1636,10 +1758,12 @@ c3nav = {
_map_moved: function () {
c3nav.update_map_state();
c3nav.update_location_labels();
c3nav._update_loadinfo_labels();
},
_map_zoomed: function () {
c3nav.update_map_state();
c3nav.update_location_labels();
c3nav._update_loadinfo_labels();
},
icons: {},
create_icons: function () {
@ -1852,7 +1976,8 @@ c3nav = {
c3nav._fetch_updates_timer = null;
c3nav_api.get('updates/fetch')
.then(c3nav._fetch_updates_callback)
.catch(() => {
.catch(err => {
console.error(err);
c3nav._fetch_updates_failure_count++;
const waittime = Math.min(5 + c3nav._fetch_updates_failure_count * 5, 120);
c3nav.schedule_fetch_updates(waittime * 1000);
@ -2033,7 +2158,8 @@ c3nav = {
ibeacon_peers: ibeacon_peers,
})
.then(data => c3nav._set_user_location(data.location))
.catch(() => {
.catch(err => {
console.error(err);
c3nav._set_user_location(null);
c3nav._last_scan = Date.now() + 20000
});
@ -2287,6 +2413,7 @@ LevelControl = L.Control.extend({
this.setLevel(e.target.level);
c3nav.update_map_state();
c3nav.update_location_labels();
c3nav._update_loadinfo_labels();
},
finalize: function () {
@ -2326,107 +2453,77 @@ UserLocationControl = L.Control.extend({
}
});
LabelControl = L.Control.extend({
ToggleControl = L.Control.extend({
options: {
position: 'bottomright',
addClasses: ''
addClasses: '',
initialOn: false,
storageId: null,
enabledIcon: null,
disabledIcon: null,
onEnable: null,
onDisable: null,
},
onAdd: function () {
this._container = L.DomUtil.create('div', 'leaflet-control-labels leaflet-bar ' + this.options.addClasses);
this.toggle = this.toggle.bind(this);
this.toggleOn = this.toggleOn.bind(this);
this.toggleOff = this.toggleOff.bind(this);
this._container = L.DomUtil.create('div', 'leaflet-control-toggle leaflet-bar ' + this.options.addClasses);
this._button = L.DomUtil.create('a', 'material-symbols', this._container);
$(this._button).click(this.toggleLabels).dblclick(function (e) {
$(this._button).click(this.toggle).dblclick(function (e) {
e.stopPropagation();
});
this._button.innerText = c3nav._map_material_icon('label');
this._button.innerText = this.options.enabledIcon;
this._button.href = '#';
this._button.classList.toggle('control-disabled', false);
this.labelsActive = true;
if (localStorageWrapper.getItem('hideLabels')) {
this.hideLabels();
let initialOn = this.options.initialOn;
if (this.options.storageId) {
initialOn = !localStorageWrapper.getItem(`c3nav.toggle-control.${this.options.storageId}.hide`);
}
window.setTimeout(() => {
if (initialOn) {
this.toggleOn();
} else {
this.toggleOff();
}
}, 1);
return this._container;
},
toggleLabels: function (e) {
toggle: function (e) {
if (e) e.preventDefault();
if (c3nav._labelControl.labelsActive) {
c3nav._labelControl.hideLabels();
if (this.enabled) {
this.toggleOff();
} else {
c3nav._labelControl.showLabels();
this.toggleOn();
}
},
showLabels: function () {
if (this.labelsActive) return;
c3nav._labelLayer.addTo(c3nav.map);
this._button.innerText = c3nav._map_material_icon('label');
toggleOn: function () {
if (this.enabled === true) return;
this.enabled = true;
if (this.options.onEnable) {
this.options.onEnable();
}
this._button.innerText = this.options.enabledIcon;
this._button.classList.toggle('control-disabled', false);
this.labelsActive = true;
localStorageWrapper.removeItem('hideLabels');
c3nav.update_location_labels();
localStorageWrapper.removeItem(`c3nav.toggle-control.${this.options.storageId}.hide`);
},
hideLabels: function () {
if (!this.labelsActive) return;
c3nav._labelLayer.clearLayers();
c3nav._labelLayer.remove();
this._button.innerText = c3nav._map_material_icon('label_outline');
this._button.classList.toggle('control-disabled', true);
this.labelsActive = false;
localStorageWrapper.setItem('hideLabels', true);
}
});
SquareGridControl = L.Control.extend({
options: {
position: 'bottomright',
addClasses: ''
},
onAdd: function () {
this._container = L.DomUtil.create('div', 'leaflet-control-grid-layer leaflet-bar ' + this.options.addClasses);
this._button = L.DomUtil.create('a', 'material-symbols', this._container);
$(this._button).click(this.toggleGrid).dblclick(function (e) {
e.stopPropagation();
});
this._button.innerText = c3nav._map_material_icon('grid_off');
this._button.href = '#';
this._button.classList.toggle('control-disabled', true);
this.gridActive = false;
if (localStorageWrapper.getItem('showGrid')) {
this.showGrid();
toggleOff: function () {
if (this.enabled === false) return;
this.enabled = false;
if (this.options.onDisable) {
this.options.onDisable();
}
return this._container;
},
toggleGrid: function (e) {
if (e) e.preventDefault();
if (c3nav._gridControl.gridActive) {
c3nav._gridControl.hideGrid();
} else {
c3nav._gridControl.showGrid();
}
},
showGrid: function () {
if (this.gridActive) return;
c3nav._gridLayer.addTo(c3nav.map);
this._button.innerText = c3nav._map_material_icon('grid_on');
this._button.classList.toggle('control-disabled', false);
this.gridActive = true;
localStorageWrapper.setItem('showGrid', true);
},
hideGrid: function () {
if (!this.gridActive) return;
c3nav._gridLayer.remove();
this._button.innerText = c3nav._map_material_icon('grid_off');
this._button.innerText = this.options.disabledIcon;
this._button.classList.toggle('control-disabled', true);
this.gridActive = false;
localStorageWrapper.removeItem('showGrid');
localStorageWrapper.setItem(`c3nav.toggle-control.${this.options.storageId}.hide`, '1');
}
});
@ -2502,7 +2599,9 @@ QuestsControl = L.Control.extend({
});
}
})
.catch();
.catch(err => {
console.error(err);
});
},
hideQuests: function () {