From d9c04c7d0a504d6e0d00b8a8fb82d94e428446d8 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 5 Dec 2024 11:46:26 +0100 Subject: [PATCH 1/5] fix: do not render normal popup title in OSM template --- umap/static/umap/js/modules/rendering/template.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/umap/static/umap/js/modules/rendering/template.js b/umap/static/umap/js/modules/rendering/template.js index e0738a390..e81695de8 100644 --- a/umap/static/umap/js/modules/rendering/template.js +++ b/umap/static/umap/js/modules/rendering/template.js @@ -150,6 +150,8 @@ class GeoRSSLink extends PopupTemplate { } class OSM extends TitleMixin(PopupTemplate) { + renderTitle(feature) {} + getName(feature) { const props = feature.properties const locale = getLocale() From 430dede0dd0611c2b7030a54163d87bbea06499d Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 5 Dec 2024 11:46:52 +0100 Subject: [PATCH 2/5] fix: fix broken OSM template --- umap/static/umap/js/modules/rendering/template.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/umap/static/umap/js/modules/rendering/template.js b/umap/static/umap/js/modules/rendering/template.js index e81695de8..171b24199 100644 --- a/umap/static/umap/js/modules/rendering/template.js +++ b/umap/static/umap/js/modules/rendering/template.js @@ -162,7 +162,7 @@ class OSM extends TitleMixin(PopupTemplate) { renderBody(feature) { const props = feature.properties const body = document.createElement('div') - const title = DomUtil.add('h3', 'popup-title', container) + const title = DomUtil.add('h3', 'popup-title', body) const color = feature.getPreviewColor() title.style.backgroundColor = color const iconUrl = feature.getDynamicOption('iconUrl') From 4949fcb4a4db53b7edab6f803e5787e455470800 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 5 Dec 2024 11:53:01 +0100 Subject: [PATCH 3/5] feat: try to guess RGB from color name when element is not in DOM We often need to compute the text/icon constrast color, but often the element is not yet in the DOM, so we can't get the background easily. Let's have a fallback for that instead of trying to have "load" events everywhere. --- umap/static/umap/js/umap.core.js | 37 +++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/umap/static/umap/js/umap.core.js b/umap/static/umap/js/umap.core.js index e7cf3cd51..0ddc7cba4 100644 --- a/umap/static/umap/js/umap.core.js +++ b/umap/static/umap/js/umap.core.js @@ -170,22 +170,43 @@ L.DomUtil.contrastWCAG21 = (rgb) => { const contrast = (whiteLum + 0.05) / (lum + 0.05) return contrast > 3 ? 1 : 0 } +L.DomUtil.colorNameToHex = (str) => { + const ctx = document.createElement('canvas').getContext('2d') + ctx.fillStyle = str + return ctx.fillStyle +} +L.DomUtil.hexToRGB = (hex) => { + return hex + .replace( + /^#?([a-f\d])([a-f\d])([a-f\d])$/i, + (m, r, g, b) => `#${r}${r}${g}${g}${b}${b}` + ) + .substring(1) + .match(/.{2}/g) + .map((x) => Number.parseInt(x, 16)) +} const _CACHE_CONSTRAST = {} L.DomUtil.contrastedColor = (el, bgcolor) => { // Return 0 for black and 1 for white // bgcolor is a human color, it can be a any keyword (purpleā€¦) if (typeof _CACHE_CONSTRAST[bgcolor] !== 'undefined') return _CACHE_CONSTRAST[bgcolor] - let out = 0 let rgb = window.getComputedStyle(el).getPropertyValue('background-color') rgb = L.DomUtil.RGBRegex.exec(rgb) - if (!rgb || rgb.length !== 4) return out - rgb = [ - Number.parseInt(rgb[1], 10), - Number.parseInt(rgb[2], 10), - Number.parseInt(rgb[3], 10), - ] - out = L.DomUtil.contrastWCAG21(rgb) + if (rgb && rgb.length === 4) { + rgb = [ + Number.parseInt(rgb[1], 10), + Number.parseInt(rgb[2], 10), + Number.parseInt(rgb[3], 10), + ] + } else { + // The element may not yet be added to the DOM, so let's try + // another way + const hex = L.DomUtil.colorNameToHex(bgcolor) + rgb = L.DomUtil.hexToRGB(hex) + } + if (!rgb) return 1 + const out = L.DomUtil.contrastWCAG21(rgb) if (bgcolor) _CACHE_CONSTRAST[bgcolor] = out return out } From 6a0bc9443bc45b193cb61ce96bfa417167c1baf3 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 5 Dec 2024 11:57:48 +0100 Subject: [PATCH 4/5] feat: display an image from Panoramax in OSM template when tag is defined --- umap/static/umap/js/modules/rendering/template.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/umap/static/umap/js/modules/rendering/template.js b/umap/static/umap/js/modules/rendering/template.js index 171b24199..7300c40bb 100644 --- a/umap/static/umap/js/modules/rendering/template.js +++ b/umap/static/umap/js/modules/rendering/template.js @@ -207,6 +207,13 @@ class OSM extends TitleMixin(PopupTemplate) { Utils.loadTemplate(`
${email}
`) ) } + if (props.panoramax) { + body.appendChild( + Utils.loadTemplate( + `
` + ) + ) + } const id = props['@id'] || props.id if (id) { body.appendChild( From 82853f7ade8cc0d8e3a7ffb4841fa7cf05f2d5e2 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 5 Dec 2024 12:10:49 +0100 Subject: [PATCH 5/5] chore: add very minimal test for OSM popup template --- .../umap/js/modules/rendering/template.js | 22 +++++----- umap/tests/integration/test_popup.py | 44 +++++++++++++++++++ 2 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 umap/tests/integration/test_popup.py diff --git a/umap/static/umap/js/modules/rendering/template.js b/umap/static/umap/js/modules/rendering/template.js index 7300c40bb..05b5a533e 100644 --- a/umap/static/umap/js/modules/rendering/template.js +++ b/umap/static/umap/js/modules/rendering/template.js @@ -150,7 +150,18 @@ class GeoRSSLink extends PopupTemplate { } class OSM extends TitleMixin(PopupTemplate) { - renderTitle(feature) {} + renderTitle(feature) { + const title = DomUtil.add('h3', 'popup-title') + const color = feature.getPreviewColor() + title.style.backgroundColor = color + const iconUrl = feature.getDynamicOption('iconUrl') + const icon = Icon.makeElement(iconUrl, title) + DomUtil.addClass(icon, 'icon') + Icon.setContrast(icon, title, iconUrl, color) + if (DomUtil.contrastedColor(title, color)) title.style.color = 'white' + DomUtil.add('span', '', title, this.getName(feature)) + return title + } getName(feature) { const props = feature.properties @@ -162,15 +173,6 @@ class OSM extends TitleMixin(PopupTemplate) { renderBody(feature) { const props = feature.properties const body = document.createElement('div') - const title = DomUtil.add('h3', 'popup-title', body) - const color = feature.getPreviewColor() - title.style.backgroundColor = color - const iconUrl = feature.getDynamicOption('iconUrl') - const icon = Icon.makeElement(iconUrl, title) - DomUtil.addClass(icon, 'icon') - Icon.setContrast(icon, title, iconUrl, color) - if (DomUtil.contrastedColor(title, color)) title.style.color = 'white' - DomUtil.add('span', '', title, this.getName(feature)) const street = props['addr:street'] if (street) { const row = DomUtil.add('address', 'address', body) diff --git a/umap/tests/integration/test_popup.py b/umap/tests/integration/test_popup.py new file mode 100644 index 000000000..23d70aab0 --- /dev/null +++ b/umap/tests/integration/test_popup.py @@ -0,0 +1,44 @@ +import pytest +from playwright.sync_api import expect + +from ..base import DataLayerFactory + +pytestmark = pytest.mark.django_db + +OSM_DATA = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [2.49, 48.79]}, + "properties": { + "amenity": "restaurant", + "cuisine": "italian", + "name": "A Casa di Nonna", + "panoramax": "d811b398-d930-4cf8-95a2-0c29c34d9fca", + "phone": "+33 1 48 89 54 12", + "takeaway:covid19": "yes", + "wheelchair": "no", + "id": "node/1130849864", + }, + "id": "AzMjk", + }, + ], + "_umap_options": { + "popupTemplate": "OSM", + }, +} + + +def test_openstreetmap_popup(live_server, map, page): + DataLayerFactory(map=map, data=OSM_DATA) + page.goto(f"{live_server.url}{map.get_absolute_url()}#18/48.79/2.49") + expect(page.locator(".umap-icon-active")).to_be_hidden() + page.locator(".leaflet-marker-icon").click() + expect(page.get_by_role("heading", name="A Casa di Nonna")).to_be_visible() + expect(page.get_by_text("+33 1 48 89 54 12")).to_be_visible() + img = page.locator(".umap-popup-content img") + expect(img).to_have_attribute( + "src", + "https://api.panoramax.xyz/api/pictures/d811b398-d930-4cf8-95a2-0c29c34d9fca/sd.jpg", + )