aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2025-05-27 00:59:37 +0200
committerGuilhem Moulin <guilhem@fripost.org>2025-05-27 01:05:17 +0200
commit6c388d0b828e62562e43cecb801bb8979b593c1d (patch)
tree0ec76e8a95deeb5f336ac2a4b270460f44ce46aa
parent736bc1ff68a0e0110c36f457a97ff6976048cb3e (diff)
Popover: Dynamically generate HTML from properties.
There is no need to pre-generate and cache the HTML for all features.
-rw-r--r--main.js182
1 files changed, 93 insertions, 89 deletions
diff --git a/main.js b/main.js
index f16e08b..6f155ef 100644
--- a/main.js
+++ b/main.js
@@ -3621,12 +3621,12 @@ const [vectorLayers, featureOverlayLayer] = (function() {
featureOverlayLayer.changed();
};
const refreshPopover = function() {
- const x = overlayAttributes[overlayAttrIdx];
- updateFeatureOverlayLayer(x.layer_group, x.layer, x.id);
+ const attr = overlayAttributes[overlayAttrIdx];
+ updateFeatureOverlayLayer(attr.layer_group, attr.layer, attr.ogc_fid);
pageNum.innerHTML = (overlayAttrIdx + 1).toString();
- popover.tip.getElementsByClassName('popover-body')[0].
- replaceChildren(x.formattedContent);
+ const content = formatFeaturePropertiesToHTML(attr);
+ popover.tip.getElementsByClassName('popover-body')[0].replaceChildren(content);
};
const onClickPageChange = function(event, offset) {
const btn = event.target;
@@ -3713,6 +3713,86 @@ const [vectorLayers, featureOverlayLayer] = (function() {
header.appendChild(btnExpand);
header.appendChild(btnClose);
+ const formatFeaturePropertiesToHTML = function(properties) {
+ /* turn the properties into a fine table */
+ const table = document.createElement('table');
+ table.classList.add('table', 'table-sm', 'table-borderless', 'table-hover');
+
+ const tbody = document.createElement('tbody');
+ table.appendChild(tbody);
+
+ const def = layers[properties.layer_group + '_' + properties.layer];
+ def.popover.forEach(function([desc, key, opts]) {
+ let v = properties[key];
+ if (opts === undefined) {
+ opts = {};
+ }
+ if (opts.fn !== undefined) {
+ if (opts.fn === 'length') {
+ if (v < 1000) {
+ opts.unit = 'm';
+ } else {
+ v /= 1000.;
+ v = Math.round(v*100) / 100.;
+ opts.unit = 'km';
+ }
+ } else if (opts.fn === 'area') {
+ if (v < 10000) {
+ opts.unit = 'm²';
+ } else if (v < 10000 * 10000) {
+ v /= 10000.;
+ opts.unit = 'ha';
+ } else {
+ v /= 1000000.;
+ opts.unit = 'km²';
+ }
+ v = Math.round(v*100) / 100.;
+ } else {
+ v = opts.fn(v);
+ }
+ }
+ if (v === undefined || v === null) {
+ v = document.createTextNode('');
+ } else if (!(v instanceof HTMLElement)) {
+ if (typeof(v) === 'number' && opts.unit !== undefined) {
+ v = v.toLocaleString('sv-SE');
+ } else if (typeof(v) === 'boolean') {
+ v = v ? 'Ja' : 'Nej';
+ }
+ if (opts.unit !== undefined && v !== '') {
+ v += '\u202F' + opts.unit;
+ }
+ v = document.createTextNode(v);
+ }
+
+ const tr = document.createElement('tr');
+ tbody.appendChild(tr);
+
+ const td1 = document.createElement('td');
+ tr.appendChild(td1);
+ const textDesc = document.createTextNode(desc);
+ td1.appendChild(textDesc);
+
+ const td2 = document.createElement('td');
+ tr.appendChild(td2);
+ td2.appendChild(v);
+ if (opts.classes !== undefined) {
+ opts.classes.forEach((c) => td2.classList.add(c));
+ }
+ });
+
+ const content = document.createElement('div');
+ if (def.popoverTitle !== undefined) {
+ const h = document.createElement('h6');
+ content.appendChild(h);
+ const textNode = document.createTextNode(def.popoverTitle);
+ h.appendChild(textNode);
+ }
+
+ content.appendChild(table);
+ return content;
+ };
+
const container0 = map.getViewport().getElementsByClassName('ol-overlaycontainer-stopevent')[0];
map.on('singleclick', function(event) {
/* clear the overlay list */
@@ -3795,90 +3875,14 @@ const [vectorLayers, featureOverlayLayer] = (function() {
/* the data is received from the CGI in the order it was sent */
/* TODO optimizations on the CGI would break the above assumption, so the
* decoded JSON response would need to be reordered to match fetch_body */
- overlayAttributes = data.map(function(properties) {
- /* turn the properties into a fine table */
- const table = document.createElement('table');
- table.classList.add('table', 'table-sm', 'table-borderless', 'table-hover');
-
- const tbody = document.createElement('tbody');
- table.appendChild(tbody);
-
- const def = layers[properties.layer_group + '_' + properties.layer];
- def.popover.forEach(function([desc, key, opts]) {
- let v = properties[key];
- if (opts === undefined) {
- opts = {};
- }
- if (opts.fn !== undefined) {
- if (opts.fn === 'length') {
- if (v < 1000) {
- opts.unit = 'm';
- } else {
- v /= 1000.;
- v = Math.round(v*100) / 100.;
- opts.unit = 'km';
- }
- } else if (opts.fn === 'area') {
- if (v < 10000) {
- opts.unit = 'm²';
- } else if (v < 10000 * 10000) {
- v /= 10000.;
- opts.unit = 'ha';
- } else {
- v /= 1000000.;
- opts.unit = 'km²';
- }
- v = Math.round(v*100) / 100.;
- } else {
- v = opts.fn(v);
- }
- }
- if (v === undefined || v === null) {
- v = document.createTextNode('');
- } else if (!(v instanceof HTMLElement)) {
- if (typeof(v) === 'number' && opts.unit !== undefined) {
- v = v.toLocaleString('sv-SE');
- } else if (typeof(v) === 'boolean') {
- v = v ? 'Ja' : 'Nej';
- }
- if (opts.unit !== undefined && v !== '') {
- v += '\u202F' + opts.unit;
- }
- v = document.createTextNode(v);
- }
-
- const tr = document.createElement('tr');
- tbody.appendChild(tr);
-
- const td1 = document.createElement('td');
- tr.appendChild(td1);
- const textDesc = document.createTextNode(desc);
- td1.appendChild(textDesc);
-
- const td2 = document.createElement('td');
- tr.appendChild(td2);
- td2.appendChild(v);
- if (opts.classes !== undefined) {
- opts.classes.forEach((c) => td2.classList.add(c));
- }
- });
-
- const content = document.createElement('div');
- if (def.popoverTitle !== undefined) {
- const h = document.createElement('h6');
- content.appendChild(h);
- const textNode = document.createTextNode(def.popoverTitle);
- h.appendChild(textNode);
+ overlayAttributes = data
+ if (overlayAttributes.length === 0) {
+ if (popover !== null) {
+ /* dispose pre-detached popover */
+ popover.dispose();
}
-
- content.appendChild(table);
- return {
- layer_group: properties.layer_group,
- layer: properties.layer,
- id: properties.ogc_fid,
- formattedContent: content
- };
- });
+ return;
+ }
pageCount.innerHTML = overlayAttributes.length.toString();
if (overlayAttributes.length >= 2) {
@@ -3893,18 +3897,18 @@ const [vectorLayers, featureOverlayLayer] = (function() {
popupOverlay.setPosition(event.coordinate);
const attr = overlayAttributes[0];
+ updateFeatureOverlayLayer(attr.layer_group, attr.layer, attr.ogc_fid);
popover = new Popover(popup, {
template: '<div class="popover" role="tooltip"><div class="popover-arrow"></div>' +
'<div class="popover-header"></div><div class="popover-body"></div></div>',
title: header,
- content: attr.formattedContent,
+ content: formatFeaturePropertiesToHTML(attr),
html: true,
placement: 'right',
fallbackPlacements: ['right', 'left', 'bottom', 'top'],
container: container0,
});
popover.show();
- updateFeatureOverlayLayer(attr.layer_group, attr.layer, attr.id);
}
else if (popover.tip.classList.contains('popover-detached')) {
/* update existing detached mode popover */