From 72758772ed7f250a2f4cb60c4c2ba8faf09db5e6 Mon Sep 17 00:00:00 2001 From: David Straub Date: Wed, 10 Jun 2026 16:47:08 +0000 Subject: [PATCH 01/10] Use raster tiles for map overlays --- src/api.js | 6 ++ src/components/GrampsjsMap.js | 42 +++--------- src/components/GrampsjsMapTileLayer.js | 94 ++++++++++++++++++++++++++ src/views/GrampsjsViewMap.js | 14 ++-- 4 files changed, 113 insertions(+), 43 deletions(-) create mode 100644 src/components/GrampsjsMapTileLayer.js diff --git a/src/api.js b/src/api.js index 1b4fda6c..2cabd748 100644 --- a/src/api.js +++ b/src/api.js @@ -618,6 +618,12 @@ export function getReportUrl(id, options) { return `${__APIHOST__}/api/reports/${id}/file?jwt=${jwt}&${queryParam}` } +export function getTileUrl(handle) { + const jwt = localStorage.getItem('access_token') + const base = `${__APIHOST__}/api/media/${handle}/tile/{z}/{x}/{y}` + return jwt ? `${base}?jwt=${jwt}` : base +} + export function getMediaUrl(handle, download = false) { const jwt = localStorage.getItem('access_token') if (jwt === null) { diff --git a/src/components/GrampsjsMap.js b/src/components/GrampsjsMap.js index bb3b6147..5c0903b8 100644 --- a/src/components/GrampsjsMap.js +++ b/src/components/GrampsjsMap.js @@ -174,7 +174,6 @@ class GrampsjsMap extends GrampsjsAppStateMixin(LitElement) { [this.longMax, this.latMax], ]) } - this._reAddOverlays() this._slottedChildren .filter(el => typeof el.addToMap === 'function') .forEach(el => el.addToMap(this._map)) @@ -248,25 +247,16 @@ class GrampsjsMap extends GrampsjsAppStateMixin(LitElement) { _handleOverlayToggle(e) { const {overlay, visible} = e.detail - - const overlays = this._slottedChildren.filter( - el => el.tagName === 'GRAMPSJS-MAP-OVERLAY' - ) - - overlays.forEach(overlayElement => { - // Prefer matching by stable handle when available; fall back to title/desc for backward compatibility - const matchesByHandle = - overlay.handle && - overlayElement.handle && - overlayElement.handle === overlay.handle - const matchesByTitle = - !overlay.handle && overlayElement.title === overlay.desc - - if (matchesByHandle || matchesByTitle) { + this._slottedChildren + .filter( + el => + (overlay.handle && el.handle === overlay.handle) || + (!overlay.handle && el.title === overlay.desc) + ) + .forEach(el => { // eslint-disable-next-line no-param-reassign - overlayElement.hidden = !visible - } - }) + el.hidden = !visible + }) } _handleStyleChange(style) { @@ -286,20 +276,6 @@ class GrampsjsMap extends GrampsjsAppStateMixin(LitElement) { } : undefined ) - this._map.once('style.load', () => { - this._reAddOverlays() - }) - } - - _reAddOverlays() { - const overlays = this._slottedChildren.filter( - el => el.tagName === 'GRAMPSJS-MAP-OVERLAY' - ) - overlays.forEach(overlayElement => { - // After style change, MapLibre cleared all layers. Reset overlay state and re-add. - overlayElement.resetForStyleChange() - overlayElement.addOverlay() - }) } _getStyleUrl(style) { diff --git a/src/components/GrampsjsMapTileLayer.js b/src/components/GrampsjsMapTileLayer.js new file mode 100644 index 00000000..a671888b --- /dev/null +++ b/src/components/GrampsjsMapTileLayer.js @@ -0,0 +1,94 @@ +import {LitElement} from 'lit' +import {getTileUrl} from '../api.js' + +class GrampsjsMapTileLayer extends LitElement { + static get properties() { + return { + handle: {type: String}, + hidden: {type: Boolean}, + } + } + + constructor() { + super() + this.handle = '' + this.hidden = false + this._map = null + } + + // No shadow DOM — renders no UI. + createRenderRoot() { + return this + } + + get _layerId() { + return `tile-overlay-${this.handle}` + } + + // Called by GrampsjsMap after initial map load. + // Imperatively adds the tile source/layer since the initial Map() constructor + // doesn't use transformStyle. + addToMap(map) { + this._map = map + if (!this.handle) return + const layerId = this._layerId + if (!map.getSource(layerId)) { + map.addSource(layerId, { + type: 'raster', + tiles: [getTileUrl(this.handle)], + tileSize: 256, + }) + } + if (!map.getLayer(layerId)) { + map.addLayer({ + id: layerId, + type: 'raster', + source: layerId, + layout: {visibility: this.hidden ? 'none' : 'visible'}, + }) + } + } + + // Called by GrampsjsMap inside setStyle's transformStyle callback so the + // tile source/layer are part of the new style spec from its very first frame. + getTransformStyleContribution(_prev, next) { + if (!this.handle) return next + const layerId = this._layerId + return { + ...next, + sources: { + ...next.sources, + [layerId]: { + type: 'raster', + tiles: [getTileUrl(this.handle)], + tileSize: 256, + maxzoom: 18, + }, + }, + layers: [ + ...next.layers, + { + id: layerId, + type: 'raster', + source: layerId, + layout: {visibility: this.hidden ? 'none' : 'visible'}, + }, + ], + } + } + + updated(changed) { + if (changed.has('hidden') && this._map) { + const layerId = this._layerId + if (this._map.getLayer(layerId)) { + this._map.setLayoutProperty( + layerId, + 'visibility', + this.hidden ? 'none' : 'visible' + ) + } + } + } +} + +window.customElements.define('grampsjs-map-tile-layer', GrampsjsMapTileLayer) diff --git a/src/views/GrampsjsViewMap.js b/src/views/GrampsjsViewMap.js index a619f25f..08ef5f44 100644 --- a/src/views/GrampsjsViewMap.js +++ b/src/views/GrampsjsViewMap.js @@ -7,7 +7,7 @@ import '../components/GrampsjsMapPlacesLayer.js' import '../components/GrampsjsMapSearchbox.js' import '../components/GrampsjsMapTimeSlider.js' import '../components/GrampsjsPlaceBox.js' -import {getMediaUrl} from '../api.js' +import '../components/GrampsjsMapTileLayer.js' import {isDateBetweenYears, getGregorianYears} from '../util.js' import {GrampsjsStaleDataMixin} from '../mixins/GrampsjsStaleDataMixin.js' @@ -352,17 +352,11 @@ export class GrampsjsViewMap extends GrampsjsStaleDataMixin(GrampsjsView) { // eslint-disable-next-line class-methods-use-this _renderMapLayer(obj) { - const bounds = obj.attribute_list.filter( - attr => attr.type === 'map:bounds' - )[0].value return html` - + ?hidden="${this._hiddenOverlaysHandles.includes(obj.handle)}" + > ` } From 2ec30210ab84f3916fdfca1273e5822aaff3da92 Mon Sep 17 00:00:00 2001 From: David Straub Date: Wed, 10 Jun 2026 20:06:43 +0000 Subject: [PATCH 02/10] Address comment --- src/components/GrampsjsMapTileLayer.js | 10 ++++++++++ src/views/GrampsjsViewMap.js | 1 - 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/GrampsjsMapTileLayer.js b/src/components/GrampsjsMapTileLayer.js index a671888b..5a3c8040 100644 --- a/src/components/GrampsjsMapTileLayer.js +++ b/src/components/GrampsjsMapTileLayer.js @@ -37,6 +37,7 @@ class GrampsjsMapTileLayer extends LitElement { type: 'raster', tiles: [getTileUrl(this.handle)], tileSize: 256, + maxzoom: 18, }) } if (!map.getLayer(layerId)) { @@ -77,6 +78,15 @@ class GrampsjsMapTileLayer extends LitElement { } } + disconnectedCallback() { + super.disconnectedCallback() + if (this._map) { + const layerId = this._layerId + if (this._map.getLayer(layerId)) this._map.removeLayer(layerId) + if (this._map.getSource(layerId)) this._map.removeSource(layerId) + } + } + updated(changed) { if (changed.has('hidden') && this._map) { const layerId = this._layerId diff --git a/src/views/GrampsjsViewMap.js b/src/views/GrampsjsViewMap.js index 08ef5f44..ddc13f63 100644 --- a/src/views/GrampsjsViewMap.js +++ b/src/views/GrampsjsViewMap.js @@ -350,7 +350,6 @@ export class GrampsjsViewMap extends GrampsjsStaleDataMixin(GrampsjsView) { return html` ${this._dataLayers.map(obj => this._renderMapLayer(obj))} ` } - // eslint-disable-next-line class-methods-use-this _renderMapLayer(obj) { return html` Date: Wed, 10 Jun 2026 20:24:45 +0000 Subject: [PATCH 03/10] Refactor map overlay --- src/components/GrampsjsMapOverlay.js | 148 ++++++++++++--------------- 1 file changed, 63 insertions(+), 85 deletions(-) diff --git a/src/components/GrampsjsMapOverlay.js b/src/components/GrampsjsMapOverlay.js index db608b9b..d0967445 100644 --- a/src/components/GrampsjsMapOverlay.js +++ b/src/components/GrampsjsMapOverlay.js @@ -13,7 +13,6 @@ class GrampsjsMapOverlay extends LitElement { title: {type: String}, handle: {type: String}, hidden: {type: Boolean}, - _overlay: {type: String, attribute: false}, } } @@ -25,7 +24,26 @@ class GrampsjsMapOverlay extends LitElement { this.handle = '' this.hidden = false this.bounds = [] - this._overlay = '' + } + + get _layerId() { + if (this.handle) return `overlay-${this.handle}` + if (this.title) return `overlay-${this.title.replace(/\s+/g, '-')}` + return '' + } + + // MapLibre expects coordinates in order: top-left, top-right, bottom-right, bottom-left + _getCoordinates() { + if (!this.bounds || this.bounds.length !== 2) return null + let [[y0, x0], [y1, x1]] = this.bounds + if (y0 < y1) [y0, y1] = [y1, y0] + if (x0 > x1) [x0, x1] = [x1, x0] + return [ + [x0, y0], // top left [lng, lat] + [x1, y0], // top right + [x1, y1], // bottom right + [x0, y1], // bottom left + ] } firstUpdated() { @@ -36,97 +54,41 @@ class GrampsjsMapOverlay extends LitElement { } addOverlay() { - if (!this._map || !this.url || !this.bounds || this.bounds.length !== 2) - return - - // Don't add if overlay is hidden - if (this.hidden) { - return - } - - // Do nothing if overlay already exists - if (this._overlay && this._map.getLayer(this._overlay)) { - return - } + if (!this._map || !this.url || !this._layerId) return + if (this.hidden) return + if (this._map.getLayer(this._layerId)) return - // Wait for style to be loaded before adding source/layer const addOverlayWhenReady = () => { - // Don't add if hidden (could have changed while waiting) - if (this.hidden) { - return - } - - // Generate stable ID if not already set - if (!this._overlay) { - if (this.handle) { - // Prefer handle-based ID for stability across re-renders - this._overlay = `overlay-${this.handle}` - } else if (this.title) { - // Fall back to title-based ID (less stable if title changes) - this._overlay = `overlay-${this.title.replace(/\s+/g, '-')}` - } else { - // Last resort: random ID (not stable across re-renders) - this._overlay = `overlay-${Math.random().toString(36).substr(2, 9)}` - } - } - - // Check if already added (shouldn't happen but be safe) - if (this._map.getSource(this._overlay)) { - return - } - - // MapLibre expects coordinates in order: top-left, top-right, bottom-right, bottom-left - // Fix: ensure bounds[0] is top-left (northwest), bounds[1] is bottom-right (southeast) - // If bounds are [south, west], [north, east], swap as needed - let [[y0, x0], [y1, x1]] = this.bounds - // Ensure y0 > y1 (top > bottom) - if (y0 < y1) { - ;[y0, y1] = [y1, y0] - } - // Ensure x0 < x1 (left < right) - if (x0 > x1) { - ;[x0, x1] = [x1, x0] - } - this._map.addSource(this._overlay, { + if (this.hidden) return + if (this._map.getSource(this._layerId)) return + const coordinates = this._getCoordinates() + if (!coordinates) return + this._map.addSource(this._layerId, { type: 'image', url: this.url, - coordinates: [ - [x0, y0], // top left [lng, lat] - [x1, y0], // top right - [x1, y1], // bottom right - [x0, y1], // bottom left - ], + coordinates, }) this._map.addLayer({ - id: this._overlay, + id: this._layerId, type: 'raster', - source: this._overlay, - paint: { - 'raster-opacity': this.opacity, - }, + source: this._layerId, + paint: {'raster-opacity': this.opacity}, }) - // Bring to front - this._map.moveLayer(this._overlay) + this._map.moveLayer(this._layerId) } - // Check if style is already loaded if (this._map.isStyleLoaded()) { addOverlayWhenReady() } else { - // Wait for style to load this._map.once('styledata', addOverlayWhenReady) } } removeOverlay() { - if (this._map && this._overlay) { - if (this._map.getLayer(this._overlay)) { - this._map.removeLayer(this._overlay) - } - if (this._map.getSource(this._overlay)) { - this._map.removeSource(this._overlay) - } - } + if (!this._map || !this._layerId) return + if (this._map.getLayer(this._layerId)) this._map.removeLayer(this._layerId) + if (this._map.getSource(this._layerId)) + this._map.removeSource(this._layerId) } disconnectedCallback() { @@ -134,15 +96,36 @@ class GrampsjsMapOverlay extends LitElement { super.disconnectedCallback() } - resetForStyleChange() { - // After a style change, MapLibre has already cleared all layers/sources. - // We just need to reset our internal state so addOverlay() can recreate them. - this._overlay = '' + // Called by GrampsjsMap inside setStyle's transformStyle callback so the + // image source/layer survive style switches without a two-pass re-add. + getTransformStyleContribution(_prev, next) { + if (!this.url || !this._layerId) return next + const coordinates = this._getCoordinates() + if (!coordinates) return next + const layerId = this._layerId + return { + ...next, + sources: { + ...next.sources, + [layerId]: {type: 'image', url: this.url, coordinates}, + }, + layers: [ + ...next.layers, + { + id: layerId, + type: 'raster', + source: layerId, + layout: {visibility: this.hidden ? 'none' : 'visible'}, + paint: {'raster-opacity': this.opacity}, + }, + ], + } } updated(changed) { if (changed.has('bounds') || changed.has('opacity') || changed.has('url')) { - this.updateOverlay() + this.removeOverlay() + this.addOverlay() } else if (changed.has('hidden')) { if (this.hidden) { this.removeOverlay() @@ -151,11 +134,6 @@ class GrampsjsMapOverlay extends LitElement { } } } - - updateOverlay() { - this.removeOverlay() - this.addOverlay() - } } window.customElements.define('grampsjs-map-overlay', GrampsjsMapOverlay) From 9a2dc47fad064e80437d3266d1fec4b51678a1fb Mon Sep 17 00:00:00 2001 From: David Straub Date: Thu, 11 Jun 2026 06:55:24 +0000 Subject: [PATCH 04/10] Address comments --- src/components/GrampsjsMapOverlay.js | 26 ++++++++++++++++++++++---- src/components/GrampsjsMapTileLayer.js | 9 +++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/components/GrampsjsMapOverlay.js b/src/components/GrampsjsMapOverlay.js index d0967445..8c196616 100644 --- a/src/components/GrampsjsMapOverlay.js +++ b/src/components/GrampsjsMapOverlay.js @@ -26,12 +26,16 @@ class GrampsjsMapOverlay extends LitElement { this.bounds = [] } - get _layerId() { - if (this.handle) return `overlay-${this.handle}` - if (this.title) return `overlay-${this.title.replace(/\s+/g, '-')}` + _layerIdFor(handle, title) { + if (handle) return `overlay-${handle}` + if (title) return `overlay-${title.replace(/\s+/g, '-')}` return '' } + get _layerId() { + return this._layerIdFor(this.handle, this.title) + } + // MapLibre expects coordinates in order: top-left, top-right, bottom-right, bottom-left _getCoordinates() { if (!this.bounds || this.bounds.length !== 2) return null @@ -123,7 +127,21 @@ class GrampsjsMapOverlay extends LitElement { } updated(changed) { - if (changed.has('bounds') || changed.has('opacity') || changed.has('url')) { + if (changed.has('handle') || changed.has('title')) { + const oldId = this._layerIdFor( + changed.has('handle') ? changed.get('handle') : this.handle, + changed.has('title') ? changed.get('title') : this.title + ) + if (oldId && this._map) { + if (this._map.getLayer(oldId)) this._map.removeLayer(oldId) + if (this._map.getSource(oldId)) this._map.removeSource(oldId) + } + this.addOverlay() + } else if ( + changed.has('bounds') || + changed.has('opacity') || + changed.has('url') + ) { this.removeOverlay() this.addOverlay() } else if (changed.has('hidden')) { diff --git a/src/components/GrampsjsMapTileLayer.js b/src/components/GrampsjsMapTileLayer.js index 5a3c8040..5332b961 100644 --- a/src/components/GrampsjsMapTileLayer.js +++ b/src/components/GrampsjsMapTileLayer.js @@ -88,6 +88,15 @@ class GrampsjsMapTileLayer extends LitElement { } updated(changed) { + if (changed.has('handle') && this._map) { + const oldHandle = changed.get('handle') + if (oldHandle) { + const oldLayerId = `tile-overlay-${oldHandle}` + if (this._map.getLayer(oldLayerId)) this._map.removeLayer(oldLayerId) + if (this._map.getSource(oldLayerId)) this._map.removeSource(oldLayerId) + } + this.addToMap(this._map) + } if (changed.has('hidden') && this._map) { const layerId = this._layerId if (this._map.getLayer(layerId)) { From 8c79adeed512655009f967760c475e9266f2c242 Mon Sep 17 00:00:00 2001 From: David Straub Date: Fri, 12 Jun 2026 13:26:34 +0000 Subject: [PATCH 05/10] Address comment --- src/components/GrampsjsMapOverlay.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/GrampsjsMapOverlay.js b/src/components/GrampsjsMapOverlay.js index 8c196616..044672ff 100644 --- a/src/components/GrampsjsMapOverlay.js +++ b/src/components/GrampsjsMapOverlay.js @@ -147,6 +147,8 @@ class GrampsjsMapOverlay extends LitElement { } else if (changed.has('hidden')) { if (this.hidden) { this.removeOverlay() + } else if (this._map && this._map.getLayer(this._layerId)) { + this._map.setLayoutProperty(this._layerId, 'visibility', 'visible') } else { this.addOverlay() } From 75894fb36eaacc0ae424de35350a05e82f0f3fe3 Mon Sep 17 00:00:00 2001 From: David Straub Date: Fri, 12 Jun 2026 14:19:12 +0000 Subject: [PATCH 06/10] Do not move map on pin click --- src/views/GrampsjsViewMap.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/GrampsjsViewMap.js b/src/views/GrampsjsViewMap.js index ddc13f63..584c4099 100644 --- a/src/views/GrampsjsViewMap.js +++ b/src/views/GrampsjsViewMap.js @@ -308,16 +308,17 @@ export class GrampsjsViewMap extends GrampsjsStaleDataMixin(GrampsjsView) { _handleMapMarkerClicked(e) { const place = this._dataPlaces.find(p => p.handle === e.detail.handle) - if (place) this._handlePlaceSelected(place) + if (place) this._handlePlaceSelected(place, {flyTo: false}) } - _handlePlaceSelected(object) { + _handlePlaceSelected(object, {flyTo = true} = {}) { this._dataSearch = [] this._valueSearch = object.profile.name this._handlesHighlight = [object.handle] const searchbox = this.renderRoot.querySelector('grampsjs-map-searchbox') searchbox?.showDetails() if ( + flyTo && object.profile.lat != null && object.profile.long != null && !(object.profile.lat === 0 && object.profile.long === 0) From c3692bd51c64262252d2f1f6d133c2355d14805f Mon Sep 17 00:00:00 2001 From: David Straub Date: Fri, 12 Jun 2026 15:04:28 +0000 Subject: [PATCH 07/10] Address comment --- src/components/GrampsjsMap.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/GrampsjsMap.js b/src/components/GrampsjsMap.js index 5c0903b8..520f1644 100644 --- a/src/components/GrampsjsMap.js +++ b/src/components/GrampsjsMap.js @@ -60,7 +60,7 @@ class GrampsjsMap extends GrampsjsAppStateMixin(LitElement) { style="width:${this.width}; height:${this.height};" >
- +
${this.layerSwitcher ? this._renderLayerSwitcher() : html`
`} @@ -197,6 +197,14 @@ class GrampsjsMap extends GrampsjsAppStateMixin(LitElement) { return slot.assignedElements({flatten: true}) } + // Handles children added after the map's load event (e.g. async data layers). + _onSlotChange() { + if (!this._map) return + this._slottedChildren + .filter(el => typeof el.addToMap === 'function') + .forEach(el => el.addToMap(this._map)) + } + panTo(latitude, longitude) { if (this._map !== undefined) { this._map.panTo([longitude, latitude]) From d9faf84de6e593913e233899f58412b1ddd39716 Mon Sep 17 00:00:00 2001 From: David Straub Date: Fri, 12 Jun 2026 17:39:25 +0000 Subject: [PATCH 08/10] Small refactor --- src/components/GrampsjsFormEditMapLayer.js | 1 + src/components/GrampsjsMap.js | 2 +- src/components/GrampsjsMapTileLayer.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/GrampsjsFormEditMapLayer.js b/src/components/GrampsjsFormEditMapLayer.js index c7912ce8..7ac8b123 100644 --- a/src/components/GrampsjsFormEditMapLayer.js +++ b/src/components/GrampsjsFormEditMapLayer.js @@ -159,6 +159,7 @@ class GrampsjsFormEditMapLayer extends GrampsjsObjectForm { url="${getMediaUrl(this.data.handle)}" opacity="${this.opacity}" title="${this.data.desc}" + handle="${this.data.handle}" .bounds="${this._getBounds()}" >` : '' diff --git a/src/components/GrampsjsMap.js b/src/components/GrampsjsMap.js index 520f1644..85fbeaa1 100644 --- a/src/components/GrampsjsMap.js +++ b/src/components/GrampsjsMap.js @@ -199,7 +199,7 @@ class GrampsjsMap extends GrampsjsAppStateMixin(LitElement) { // Handles children added after the map's load event (e.g. async data layers). _onSlotChange() { - if (!this._map) return + if (!this._map?.isStyleLoaded()) return this._slottedChildren .filter(el => typeof el.addToMap === 'function') .forEach(el => el.addToMap(this._map)) diff --git a/src/components/GrampsjsMapTileLayer.js b/src/components/GrampsjsMapTileLayer.js index 5332b961..7f3520d3 100644 --- a/src/components/GrampsjsMapTileLayer.js +++ b/src/components/GrampsjsMapTileLayer.js @@ -97,7 +97,7 @@ class GrampsjsMapTileLayer extends LitElement { } this.addToMap(this._map) } - if (changed.has('hidden') && this._map) { + if (changed.has('hidden') && this._map?.isStyleLoaded()) { const layerId = this._layerId if (this._map.getLayer(layerId)) { this._map.setLayoutProperty( From 025196fc8245d0001cb6cce4c2a277687e8c9362 Mon Sep 17 00:00:00 2001 From: David Straub Date: Fri, 12 Jun 2026 17:53:50 +0000 Subject: [PATCH 09/10] Fix --- src/components/GrampsjsMapTileLayer.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/components/GrampsjsMapTileLayer.js b/src/components/GrampsjsMapTileLayer.js index 7f3520d3..3c8380e4 100644 --- a/src/components/GrampsjsMapTileLayer.js +++ b/src/components/GrampsjsMapTileLayer.js @@ -14,6 +14,7 @@ class GrampsjsMapTileLayer extends LitElement { this.handle = '' this.hidden = false this._map = null + this._onStyleLoad = () => this._syncVisibility() } // No shadow DOM — renders no UI. @@ -25,11 +26,24 @@ class GrampsjsMapTileLayer extends LitElement { return `tile-overlay-${this.handle}` } + _syncVisibility() { + if (!this._map || !this.handle) return + const layerId = this._layerId + if (this._map.getLayer(layerId)) { + this._map.setLayoutProperty( + layerId, + 'visibility', + this.hidden ? 'none' : 'visible' + ) + } + } + // Called by GrampsjsMap after initial map load. // Imperatively adds the tile source/layer since the initial Map() constructor // doesn't use transformStyle. addToMap(map) { this._map = map + map.on('style.load', this._onStyleLoad) if (!this.handle) return const layerId = this._layerId if (!map.getSource(layerId)) { @@ -81,6 +95,7 @@ class GrampsjsMapTileLayer extends LitElement { disconnectedCallback() { super.disconnectedCallback() if (this._map) { + this._map.off('style.load', this._onStyleLoad) const layerId = this._layerId if (this._map.getLayer(layerId)) this._map.removeLayer(layerId) if (this._map.getSource(layerId)) this._map.removeSource(layerId) @@ -98,14 +113,7 @@ class GrampsjsMapTileLayer extends LitElement { this.addToMap(this._map) } if (changed.has('hidden') && this._map?.isStyleLoaded()) { - const layerId = this._layerId - if (this._map.getLayer(layerId)) { - this._map.setLayoutProperty( - layerId, - 'visibility', - this.hidden ? 'none' : 'visible' - ) - } + this._syncVisibility() } } } From e248d2a6393bb9de0ef5cc77aeb9655fa3d793b3 Mon Sep 17 00:00:00 2001 From: David Straub Date: Fri, 12 Jun 2026 18:08:55 +0000 Subject: [PATCH 10/10] Fixes --- src/api.js | 2 +- src/components/GrampsjsMapOverlay.js | 16 ++++++++++++---- src/components/GrampsjsMapTileLayer.js | 1 + 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/api.js b/src/api.js index 2cabd748..b28764b5 100644 --- a/src/api.js +++ b/src/api.js @@ -621,7 +621,7 @@ export function getReportUrl(id, options) { export function getTileUrl(handle) { const jwt = localStorage.getItem('access_token') const base = `${__APIHOST__}/api/media/${handle}/tile/{z}/{x}/{y}` - return jwt ? `${base}?jwt=${jwt}` : base + return jwt === null ? base : `${base}?jwt=${jwt}` } export function getMediaUrl(handle, download = false) { diff --git a/src/components/GrampsjsMapOverlay.js b/src/components/GrampsjsMapOverlay.js index 044672ff..bfb8fe8d 100644 --- a/src/components/GrampsjsMapOverlay.js +++ b/src/components/GrampsjsMapOverlay.js @@ -24,6 +24,13 @@ class GrampsjsMapOverlay extends LitElement { this.handle = '' this.hidden = false this.bounds = [] + this._onStyleLoad = () => { + if (this.hidden) { + this.removeOverlay() + } else { + this.addOverlay() + } + } } _layerIdFor(handle, title) { @@ -52,9 +59,9 @@ class GrampsjsMapOverlay extends LitElement { firstUpdated() { this._map = this.parentElement._map - if (!this.hidden) { - this.addOverlay() - } + this._map.off('style.load', this._onStyleLoad) + this._map.on('style.load', this._onStyleLoad) + if (!this.hidden) this.addOverlay() } addOverlay() { @@ -96,6 +103,7 @@ class GrampsjsMapOverlay extends LitElement { } disconnectedCallback() { + if (this._map) this._map.off('style.load', this._onStyleLoad) this.removeOverlay() super.disconnectedCallback() } @@ -103,7 +111,7 @@ class GrampsjsMapOverlay extends LitElement { // Called by GrampsjsMap inside setStyle's transformStyle callback so the // image source/layer survive style switches without a two-pass re-add. getTransformStyleContribution(_prev, next) { - if (!this.url || !this._layerId) return next + if (!this.url || !this._layerId || this.hidden) return next const coordinates = this._getCoordinates() if (!coordinates) return next const layerId = this._layerId diff --git a/src/components/GrampsjsMapTileLayer.js b/src/components/GrampsjsMapTileLayer.js index 3c8380e4..d131ab46 100644 --- a/src/components/GrampsjsMapTileLayer.js +++ b/src/components/GrampsjsMapTileLayer.js @@ -43,6 +43,7 @@ class GrampsjsMapTileLayer extends LitElement { // doesn't use transformStyle. addToMap(map) { this._map = map + map.off('style.load', this._onStyleLoad) map.on('style.load', this._onStyleLoad) if (!this.handle) return const layerId = this._layerId