forked from MapComplete/MapComplete
		
	Performance hacks
This commit is contained in:
		
							parent
							
								
									686fb29ed3
								
							
						
					
					
						commit
						7090a5ceb8
					
				
					 15 changed files with 167 additions and 93 deletions
				
			
		|  | @ -39,20 +39,6 @@ export default class SelectedFeatureHandler { | ||||||
|         hash.addCallback(() => self.setSelectedElementFromHash()) |         hash.addCallback(() => self.setSelectedElementFromHash()) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         // IF the selected element changes, set the hash correctly
 |  | ||||||
|         state.selectedElement.addCallback(feature => { |  | ||||||
|             if (feature === undefined) { |  | ||||||
|                 if (!SelectedFeatureHandler._no_trigger_on.has(hash.data)) { |  | ||||||
|                     hash.setData("") |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             const h = feature?.properties?.id; |  | ||||||
|             if (h !== undefined) { |  | ||||||
|                 hash.setData(h) |  | ||||||
|             } |  | ||||||
|         }) |  | ||||||
| 
 |  | ||||||
|         state.featurePipeline?.newDataLoadedSignal?.addCallbackAndRunD(_ => { |         state.featurePipeline?.newDataLoadedSignal?.addCallbackAndRunD(_ => { | ||||||
|             // New data was loaded. In initial startup, the hash might be set (via the URL) but might not be selected yet
 |             // New data was loaded. In initial startup, the hash might be set (via the URL) but might not be selected yet
 | ||||||
|             if (hash.data === undefined || SelectedFeatureHandler._no_trigger_on.has(hash.data)) { |             if (hash.data === undefined || SelectedFeatureHandler._no_trigger_on.has(hash.data)) { | ||||||
|  |  | ||||||
|  | @ -201,9 +201,10 @@ export interface TiledFeatureSourceOptions { | ||||||
|     readonly minZoomLevel?: number, |     readonly minZoomLevel?: number, | ||||||
|     /** |     /** | ||||||
|      * IF minZoomLevel is set, and if a feature runs through a tile boundary, it would normally be duplicated. |      * IF minZoomLevel is set, and if a feature runs through a tile boundary, it would normally be duplicated. | ||||||
|      * Setting 'dontEnforceMinZoomLevel' will still allow bigger zoom levels for those features |      * Setting 'dontEnforceMinZoomLevel' will still allow bigger zoom levels for those features. | ||||||
|  |      * If 'pick_first' is set, the feature will not be duplicated but set to some tile | ||||||
|      */ |      */ | ||||||
|     readonly dontEnforceMinZoom?: boolean, |     readonly dontEnforceMinZoom?: boolean | "pick_first", | ||||||
|     readonly registerTile?: (tile: TiledFeatureSource & FeatureSourceForLayer & Tiled) => void, |     readonly registerTile?: (tile: TiledFeatureSource & FeatureSourceForLayer & Tiled) => void, | ||||||
|     readonly layer?: FilteredLayer |     readonly layer?: FilteredLayer | ||||||
| } | } | ||||||
|  | @ -56,6 +56,7 @@ export default class MetaTagging { | ||||||
|             const feature = ff.feature |             const feature = ff.feature | ||||||
|             const freshness = ff.freshness |             const freshness = ff.freshness | ||||||
|             let somethingChanged = false |             let somethingChanged = false | ||||||
|  |             let definedTags = new Set(Object.getOwnPropertyNames( feature.properties )) | ||||||
|             for (const metatag of metatagsToApply) { |             for (const metatag of metatagsToApply) { | ||||||
|                 try { |                 try { | ||||||
|                     if (!metatag.keys.some(key => feature.properties[key] === undefined)) { |                     if (!metatag.keys.some(key => feature.properties[key] === undefined)) { | ||||||
|  | @ -64,8 +65,11 @@ export default class MetaTagging { | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     if (metatag.isLazy) { |                     if (metatag.isLazy) { | ||||||
|  |                         if(!metatag.keys.some(key => !definedTags.has(key))) { | ||||||
|  |                             // All keys are defined - lets skip!
 | ||||||
|  |                             continue | ||||||
|  |                         } | ||||||
|                         somethingChanged = true; |                         somethingChanged = true; | ||||||
| 
 |  | ||||||
|                         metatag.applyMetaTagsOnFeature(feature, freshness, layer, state) |                         metatag.applyMetaTagsOnFeature(feature, freshness, layer, state) | ||||||
|                     } else { |                     } else { | ||||||
|                         const newValueAdded = metatag.applyMetaTagsOnFeature(feature, freshness, layer, state) |                         const newValueAdded = metatag.applyMetaTagsOnFeature(feature, freshness, layer, state) | ||||||
|  | @ -84,12 +88,13 @@ export default class MetaTagging { | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (layerFuncs !== undefined) { |             if (layerFuncs !== undefined) { | ||||||
|  |                 let retaggingChanged = false; | ||||||
|                 try { |                 try { | ||||||
|                     layerFuncs(params, feature) |                     retaggingChanged = layerFuncs(params, feature) | ||||||
|                 } catch (e) { |                 } catch (e) { | ||||||
|                     console.error(e) |                     console.error(e) | ||||||
|                 } |                 } | ||||||
|                 somethingChanged = true |                 somethingChanged = somethingChanged || retaggingChanged | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (somethingChanged) { |             if (somethingChanged) { | ||||||
|  | @ -99,8 +104,8 @@ export default class MetaTagging { | ||||||
|         } |         } | ||||||
|         return atLeastOneFeatureChanged |         return atLeastOneFeatureChanged | ||||||
|     } |     } | ||||||
|     public static createFunctionsForFeature(layerId: string, calculatedTags: [string, string, boolean][]): ((feature: any) => void)[] { |     public static createFunctionsForFeature(layerId: string, calculatedTags: [string, string, boolean][]): ((feature: any) => boolean)[] { | ||||||
|         const functions: ((feature: any) => void)[] = []; |         const functions: ((feature: any) => boolean)[] = []; | ||||||
|          |          | ||||||
|         for (const entry of calculatedTags) { |         for (const entry of calculatedTags) { | ||||||
|             const key = entry[0] |             const key = entry[0] | ||||||
|  | @ -110,10 +115,9 @@ export default class MetaTagging { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             const calculateAndAssign = (feat) => { |             const calculateAndAssign: ((feat: any) => boolean) = (feat) => { | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|                 try { |                 try { | ||||||
|  |                     let oldValue = isStrict ? feat.properties[key] : undefined | ||||||
|                     let result = new Function("feat", "return " + code + ";")(feat); |                     let result = new Function("feat", "return " + code + ";")(feat); | ||||||
|                     if (result === "") { |                     if (result === "") { | ||||||
|                         result === undefined |                         result === undefined | ||||||
|  | @ -124,7 +128,7 @@ export default class MetaTagging { | ||||||
|                     } |                     } | ||||||
|                     delete feat.properties[key] |                     delete feat.properties[key] | ||||||
|                     feat.properties[key] = result; |                     feat.properties[key] = result; | ||||||
|                     return result; |                     return result === oldValue; | ||||||
|                 }catch(e){ |                 }catch(e){ | ||||||
|                     if (MetaTagging.errorPrintCount < MetaTagging.stopErrorOutputAt) { |                     if (MetaTagging.errorPrintCount < MetaTagging.stopErrorOutputAt) { | ||||||
|                         console.warn("Could not calculate a " + (isStrict ? "strict " : "") + " calculated tag for key " + key + " defined by " + code + " (in layer" + layerId + ") due to \n" + e + "\n. Are you the theme creator? Doublecheck your code. Note that the metatags might not be stable on new features", e, e.stack) |                         console.warn("Could not calculate a " + (isStrict ? "strict " : "") + " calculated tag for key " + key + " defined by " + code + " (in layer" + layerId + ") due to \n" + e + "\n. Are you the theme creator? Doublecheck your code. Note that the metatags might not be stable on new features", e, e.stack) | ||||||
|  | @ -133,6 +137,7 @@ export default class MetaTagging { | ||||||
|                             console.error("Got ", MetaTagging.stopErrorOutputAt, " errors calculating this metatagging - stopping output now") |                             console.error("Got ", MetaTagging.stopErrorOutputAt, " errors calculating this metatagging - stopping output now") | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|  |                     return false; | ||||||
|                 } |                 } | ||||||
|             }  |             }  | ||||||
|                  |                  | ||||||
|  | @ -149,9 +154,11 @@ export default class MetaTagging { | ||||||
|                     configurable: true, |                     configurable: true, | ||||||
|                     enumerable: false, // By setting this as not enumerable, the localTileSaver will _not_ calculate this
 |                     enumerable: false, // By setting this as not enumerable, the localTileSaver will _not_ calculate this
 | ||||||
|                     get: function () { |                     get: function () { | ||||||
|                         return calculateAndAssign(feature) |                         calculateAndAssign(feature) | ||||||
|  |                         return feature.properties[key] | ||||||
|                     } |                     } | ||||||
|                 }) |                 }) | ||||||
|  |                 return true | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -160,17 +167,23 @@ export default class MetaTagging { | ||||||
|         return functions; |         return functions; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static retaggingFuncCache = new Map<string, ((feature: any) => void)[]>() |     private static retaggingFuncCache = new Map<string, ((feature: any) => boolean)[]>() | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Creates the function which adds all the calculated tags to a feature. Called once per layer | ||||||
|  |      * @param layer | ||||||
|  |      * @param state | ||||||
|  |      * @private | ||||||
|  |      */ | ||||||
|     private static createRetaggingFunc(layer: LayerConfig, state): |     private static createRetaggingFunc(layer: LayerConfig, state): | ||||||
|         ((params: ExtraFuncParams, feature: any) => void) { |         ((params: ExtraFuncParams, feature: any) => boolean) { | ||||||
| 
 | 
 | ||||||
|         const calculatedTags: [string, string, boolean][] = layer.calculatedTags; |         const calculatedTags: [string, string, boolean][] = layer.calculatedTags; | ||||||
|         if (calculatedTags === undefined || calculatedTags.length === 0) { |         if (calculatedTags === undefined || calculatedTags.length === 0) { | ||||||
|             return undefined; |             return undefined; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let functions = MetaTagging.retaggingFuncCache.get(layer.id); |         let functions :((feature: any) => boolean)[] = MetaTagging.retaggingFuncCache.get(layer.id); | ||||||
|         if (functions === undefined) { |         if (functions === undefined) { | ||||||
|             functions = MetaTagging.createFunctionsForFeature(layer.id, calculatedTags) |             functions = MetaTagging.createFunctionsForFeature(layer.id, calculatedTags) | ||||||
|             MetaTagging.retaggingFuncCache.set(layer.id, functions) |             MetaTagging.retaggingFuncCache.set(layer.id, functions) | ||||||
|  | @ -192,6 +205,7 @@ export default class MetaTagging { | ||||||
|             } catch (e) { |             } catch (e) { | ||||||
|                 console.error("Invalid syntax in calculated tags or some other error: ", e) |                 console.error("Invalid syntax in calculated tags or some other error: ", e) | ||||||
|             } |             } | ||||||
|  |             return true; // Something changed
 | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -272,7 +272,7 @@ export default class SimpleMetaTaggers { | ||||||
|     public static country = new CountryTagger() |     public static country = new CountryTagger() | ||||||
|     private static isOpen = new SimpleMetaTagger( |     private static isOpen = new SimpleMetaTagger( | ||||||
|         { |         { | ||||||
|             keys: ["_isOpen", "_isOpen:description"], |             keys: ["_isOpen"], | ||||||
|             doc: "If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no')", |             doc: "If 'opening_hours' is present, it will add the current state of the feature (being 'yes' or 'no')", | ||||||
|             includesDates: true, |             includesDates: true, | ||||||
|             isLazy: true |             isLazy: true | ||||||
|  | @ -291,7 +291,8 @@ export default class SimpleMetaTaggers { | ||||||
|                     delete feature.properties._isOpen |                     delete feature.properties._isOpen | ||||||
|                     feature.properties._isOpen = undefined |                     feature.properties._isOpen = undefined | ||||||
|                     const tagsSource = state.allElements.getEventSourceById(feature.properties.id); |                     const tagsSource = state.allElements.getEventSourceById(feature.properties.id); | ||||||
|                     tagsSource.addCallbackAndRunD(tags => { |                     tagsSource | ||||||
|  |                         .addCallbackAndRunD(tags => { | ||||||
|                         if (tags.opening_hours === undefined || tags._country === undefined) { |                         if (tags.opening_hours === undefined || tags._country === undefined) { | ||||||
|                             return; |                             return; | ||||||
|                         } |                         } | ||||||
|  | @ -341,7 +342,6 @@ export default class SimpleMetaTaggers { | ||||||
|                                 } |                                 } | ||||||
|                             } |                             } | ||||||
|                             updateTags(); |                             updateTags(); | ||||||
|                             return true; // Our job is done, lets unregister!
 |  | ||||||
|                         } catch (e) { |                         } catch (e) { | ||||||
|                             console.warn("Error while parsing opening hours of ", tags.id, e); |                             console.warn("Error while parsing opening hours of ", tags.id, e); | ||||||
|                             delete tags._isOpen |                             delete tags._isOpen | ||||||
|  | @ -352,6 +352,7 @@ export default class SimpleMetaTaggers { | ||||||
|                     return undefined |                     return undefined | ||||||
|                 } |                 } | ||||||
|             }) |             }) | ||||||
|  |             return true; | ||||||
| 
 | 
 | ||||||
|         }) |         }) | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| import {FixedUiElement} from "./FixedUiElement"; | import {FixedUiElement} from "./FixedUiElement"; | ||||||
| import {Utils} from "../../Utils"; | import {Utils} from "../../Utils"; | ||||||
| import BaseUIElement from "../BaseUIElement"; | import BaseUIElement from "../BaseUIElement"; | ||||||
| import Title from "./Title"; |  | ||||||
| 
 | 
 | ||||||
| export default class Combine extends BaseUIElement { | export default class Combine extends BaseUIElement { | ||||||
|     private readonly uiElements: BaseUIElement[]; |     private readonly uiElements: BaseUIElement[]; | ||||||
|  | @ -21,6 +20,13 @@ export default class Combine extends BaseUIElement { | ||||||
|         return this.uiElements.map(el => el.AsMarkdown()).join(this.HasClass("flex-col") ? "\n\n" : " "); |         return this.uiElements.map(el => el.AsMarkdown()).join(this.HasClass("flex-col") ? "\n\n" : " "); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     Destroy() { | ||||||
|  |         super.Destroy(); | ||||||
|  |         for (const uiElement of this.uiElements) { | ||||||
|  |             uiElement.Destroy() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     protected InnerConstructElement(): HTMLElement { |     protected InnerConstructElement(): HTMLElement { | ||||||
|         const el = document.createElement("span") |         const el = document.createElement("span") | ||||||
|         try { |         try { | ||||||
|  |  | ||||||
|  | @ -106,6 +106,15 @@ export default class MinimapImplementation extends BaseUIElement implements Mini | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     Destroy() { | ||||||
|  |         super.Destroy(); | ||||||
|  |         console.warn("Decomissioning minimap", this._id) | ||||||
|  |         const mp = this.leafletMap.data | ||||||
|  |         this.leafletMap.setData(null) | ||||||
|  |         mp.off() | ||||||
|  |         mp.remove() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public async TakeScreenshot() { |     public async TakeScreenshot() { | ||||||
|         const screenshotter = new SimpleMapScreenshoter(); |         const screenshotter = new SimpleMapScreenshoter(); | ||||||
|         screenshotter.addTo(this.leafletMap.data); |         screenshotter.addTo(this.leafletMap.data); | ||||||
|  | @ -125,6 +134,13 @@ export default class MinimapImplementation extends BaseUIElement implements Mini | ||||||
|         const self = this; |         const self = this; | ||||||
|         // @ts-ignore
 |         // @ts-ignore
 | ||||||
|         const resizeObserver = new ResizeObserver(_ => { |         const resizeObserver = new ResizeObserver(_ => { | ||||||
|  |             if(wrapper.clientHeight === 0 || wrapper.clientWidth === 0){ | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             if(wrapper.offsetParent === null || window.getComputedStyle(wrapper).display === 'none'){ | ||||||
|  |                 // Not visible
 | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|             try { |             try { | ||||||
|                 self.InitMap(); |                 self.InitMap(); | ||||||
|                 self.leafletMap?.data?.invalidateSize() |                 self.leafletMap?.data?.invalidateSize() | ||||||
|  |  | ||||||
|  | @ -49,24 +49,26 @@ export default class ScrollableFullScreen extends UIElement { | ||||||
|                 Hash.hash.setData(hashToShow) |                 Hash.hash.setData(hashToShow) | ||||||
|                 self.Activate(); |                 self.Activate(); | ||||||
|             } else { |             } else { | ||||||
|                 self.clear(); |                 // Some cleanup...
 | ||||||
|  |                 ScrollableFullScreen.empty.AttachTo("fullscreen") | ||||||
|  |                 const fs = document.getElementById("fullscreen"); | ||||||
|  |                 ScrollableFullScreen._currentlyOpen?.isShown?.setData(false); | ||||||
|  |                 fs.classList.add("hidden") | ||||||
|             } |             } | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
|         Hash.hash.addCallback(hash => { |  | ||||||
|             if (!isShown.data) { |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|             if (hash === undefined || hash === "" || hash !== hashToShow) { |  | ||||||
|                 isShown.setData(false) |  | ||||||
|             } |  | ||||||
|         }) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     InnerRender(): BaseUIElement { |     InnerRender(): BaseUIElement { | ||||||
|         return this._component; |         return this._component; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     Destroy() { | ||||||
|  |         super.Destroy(); | ||||||
|  |         this._component.Destroy() | ||||||
|  |         this._fullscreencomponent.Destroy() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     Activate(): void { |     Activate(): void { | ||||||
|         this.isShown.setData(true) |         this.isShown.setData(true) | ||||||
|         this._fullscreencomponent.AttachTo("fullscreen"); |         this._fullscreencomponent.AttachTo("fullscreen"); | ||||||
|  | @ -74,14 +76,6 @@ export default class ScrollableFullScreen extends UIElement { | ||||||
|         ScrollableFullScreen._currentlyOpen = this; |         ScrollableFullScreen._currentlyOpen = this; | ||||||
|         fs.classList.remove("hidden") |         fs.classList.remove("hidden") | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     private clear() { |  | ||||||
|         ScrollableFullScreen.empty.AttachTo("fullscreen") |  | ||||||
|         const fs = document.getElementById("fullscreen"); |  | ||||||
|         ScrollableFullScreen._currentlyOpen?.isShown?.setData(false); |  | ||||||
|         fs.classList.add("hidden") |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private BuildComponent(title: BaseUIElement, content: BaseUIElement, isShown: UIEventSource<boolean>) { |     private BuildComponent(title: BaseUIElement, content: BaseUIElement, isShown: UIEventSource<boolean>) { | ||||||
|         const returnToTheMap = |         const returnToTheMap = | ||||||
|             new Combine([ |             new Combine([ | ||||||
|  | @ -93,6 +87,7 @@ export default class ScrollableFullScreen extends UIElement { | ||||||
| 
 | 
 | ||||||
|         returnToTheMap.onClick(() => { |         returnToTheMap.onClick(() => { | ||||||
|             isShown.setData(false) |             isShown.setData(false) | ||||||
|  |             Hash.hash.setData(undefined) | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
|         title.SetClass("block text-l sm:text-xl md:text-2xl w-full font-bold p-0 max-h-20vh overflow-y-auto") |         title.SetClass("block text-l sm:text-xl md:text-2xl w-full font-bold p-0 max-h-20vh overflow-y-auto") | ||||||
|  |  | ||||||
|  | @ -9,9 +9,18 @@ export class VariableUiElement extends BaseUIElement { | ||||||
|         this._contents = contents; |         this._contents = contents; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     Destroy() { | ||||||
|  |         super.Destroy(); | ||||||
|  |         this.isDestroyed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     protected InnerConstructElement(): HTMLElement { |     protected InnerConstructElement(): HTMLElement { | ||||||
|         const el = document.createElement("span"); |         const el = document.createElement("span"); | ||||||
|  |         const self = this; | ||||||
|         this._contents.addCallbackAndRun((contents) => { |         this._contents.addCallbackAndRun((contents) => { | ||||||
|  |             if(self.isDestroyed){ | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|             while (el.firstChild) { |             while (el.firstChild) { | ||||||
|                 el.removeChild(el.lastChild); |                 el.removeChild(el.lastChild); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ export default abstract class BaseUIElement { | ||||||
|     private clss: Set<string> = new Set<string>(); |     private clss: Set<string> = new Set<string>(); | ||||||
|     private style: string; |     private style: string; | ||||||
|     private _onClick: () => void; |     private _onClick: () => void; | ||||||
|  |     protected isDestroyed = false; | ||||||
| 
 | 
 | ||||||
|     public onClick(f: (() => void)) { |     public onClick(f: (() => void)) { | ||||||
|         this._onClick = f; |         this._onClick = f; | ||||||
|  | @ -150,5 +151,9 @@ export default abstract class BaseUIElement { | ||||||
|         throw "AsMarkdown is not implemented by " + this.constructor.name+"; implement it in the subclass" |         throw "AsMarkdown is not implemented by " + this.constructor.name+"; implement it in the subclass" | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     public Destroy(){ | ||||||
|  |         this.isDestroyed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     protected abstract InnerConstructElement(): HTMLElement; |     protected abstract InnerConstructElement(): HTMLElement; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -121,7 +121,7 @@ export default class LeftControls extends Combine { | ||||||
|                 "filters", |                 "filters", | ||||||
|                 guiState.filterViewIsOpened |                 guiState.filterViewIsOpened | ||||||
|             ).SetClass("rounded-lg md:floating-element-width"), |             ).SetClass("rounded-lg md:floating-element-width"), | ||||||
|             new MapControlButton(Svg.filter_svg()) |             new MapControlButton(Svg.layers_svg()) | ||||||
|                 .onClick(() => guiState.filterViewIsOpened.setData(true)), |                 .onClick(() => guiState.filterViewIsOpened.setData(true)), | ||||||
|             guiState.filterViewIsOpened |             guiState.filterViewIsOpened | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  | @ -235,4 +235,5 @@ export default class FeatureInfoBox extends ScrollableFullScreen { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -29,6 +29,11 @@ export default class ShowDataLayer { | ||||||
|     // Used to generate a fresh ID when needed
 |     // Used to generate a fresh ID when needed
 | ||||||
|     private _cleanCount = 0; |     private _cleanCount = 0; | ||||||
|     private geoLayer = undefined; |     private geoLayer = undefined; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * A collection of functions to call when the current geolayer is unregistered | ||||||
|  |      */ | ||||||
|  |     private unregister: (() => void)[] = []; | ||||||
|     private isDirty = false; |     private isDirty = false; | ||||||
|     /** |     /** | ||||||
|      * If the selected element triggers, this is used to lookup the correct layer and to open the popup |      * If the selected element triggers, this is used to lookup the correct layer and to open the popup | ||||||
|  | @ -56,25 +61,32 @@ export default class ShowDataLayer { | ||||||
|         const self = this; |         const self = this; | ||||||
| 
 | 
 | ||||||
|         options.leafletMap.addCallback(_ => { |         options.leafletMap.addCallback(_ => { | ||||||
|                 self.update(options) |                 return self.update(options) | ||||||
|             } |             } | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         this._features.features.addCallback(_ => self.update(options)); |         this._features.features.addCallback(_ => self.update(options)); | ||||||
|         options.doShowLayer?.addCallback(doShow => { |         options.doShowLayer?.addCallback(doShow => { | ||||||
|             const mp = options.leafletMap.data; |             const mp = options.leafletMap.data; | ||||||
|  |             if(mp === null){ | ||||||
|  |                 self.Destroy() | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|             if (mp == undefined) { |             if (mp == undefined) { | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|             if (doShow) { |             if (doShow) { | ||||||
|                 if (self.isDirty) { |                 if (self.isDirty) { | ||||||
|                     self.update(options) |                     return self.update(options) | ||||||
|                 } else { |                 } else { | ||||||
|                     mp.addLayer(this.geoLayer) |                     mp.addLayer(this.geoLayer) | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 if (this.geoLayer !== undefined) { |                 if (this.geoLayer !== undefined) { | ||||||
|                     mp.removeLayer(this.geoLayer) |                     mp.removeLayer(this.geoLayer) | ||||||
|  |                     this.unregister.forEach(f => f()) | ||||||
|  |                     this.unregister = [] | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -82,40 +94,50 @@ export default class ShowDataLayer { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         this._selectedElement?.addCallbackAndRunD(selected => { |         this._selectedElement?.addCallbackAndRunD(selected => { | ||||||
|             if (self._leafletMap.data === undefined) { |             self.openPopupOfSelectedElement(selected) | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|             const v = self.leafletLayersPerId.get(selected.properties.id + selected.geometry.type) |  | ||||||
|             if (v === undefined) { |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|             const leafletLayer = v.leafletlayer |  | ||||||
|             const feature = v.feature |  | ||||||
|             if (leafletLayer.getPopup().isOpen()) { |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|             if (selected.properties.id !== feature.properties.id) { |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (feature.id !== feature.properties.id) { |  | ||||||
|                 // Probably a feature which has renamed
 |  | ||||||
|                 // the feature might have as id 'node/-1' and as 'feature.properties.id' = 'the newly assigned id'. That is no good too
 |  | ||||||
|                 console.log("Not opening the popup for", feature, "as probably renamed") |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|             if (selected.geometry.type === feature.geometry.type  // If a feature is rendered both as way and as point, opening one popup might trigger the other to open, which might trigger the one to open again
 |  | ||||||
|             ) { |  | ||||||
|                 console.log("Opening popup of feature", feature) |  | ||||||
|                 leafletLayer.openPopup() |  | ||||||
|             } |  | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
|         this.update(options) |         this.update(options) | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private update(options: ShowDataLayerOptions) { |     private Destroy() { | ||||||
|  |         this.unregister.forEach(f => f()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private openPopupOfSelectedElement(selected) { | ||||||
|  |         if (selected === undefined) { | ||||||
|  |             return | ||||||
|  |         } | ||||||
|  |         if (this._leafletMap.data === undefined) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         const v = this.leafletLayersPerId.get(selected.properties.id + selected.geometry.type) | ||||||
|  |         if (v === undefined) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         const leafletLayer = v.leafletlayer | ||||||
|  |         const feature = v.feature | ||||||
|  |         if (leafletLayer.getPopup().isOpen()) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         if (selected.properties.id !== feature.properties.id) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (feature.id !== feature.properties.id) { | ||||||
|  |             // Probably a feature which has renamed
 | ||||||
|  |             // the feature might have as id 'node/-1' and as 'feature.properties.id' = 'the newly assigned id'. That is no good too
 | ||||||
|  |             console.log("Not opening the popup for", feature, "as probably renamed") | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         if (selected.geometry.type === feature.geometry.type  // If a feature is rendered both as way and as point, opening one popup might trigger the other to open, which might trigger the one to open again
 | ||||||
|  |         ) { | ||||||
|  |             leafletLayer.openPopup() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private update(options: ShowDataLayerOptions) : boolean{ | ||||||
|         if (this._features.features.data === undefined) { |         if (this._features.features.data === undefined) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  | @ -125,9 +147,13 @@ export default class ShowDataLayer { | ||||||
|         } |         } | ||||||
|         const mp = options.leafletMap.data; |         const mp = options.leafletMap.data; | ||||||
| 
 | 
 | ||||||
|  |         if(mp === null){ | ||||||
|  |             return true; // Unregister as the map is destroyed
 | ||||||
|  |         } | ||||||
|         if (mp === undefined) { |         if (mp === undefined) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |         console.trace("Updating... " + mp["_container"]?.id +" for layer "+this._layerToShow.id) | ||||||
|         this._cleanCount++ |         this._cleanCount++ | ||||||
|         // clean all the old stuff away, if any
 |         // clean all the old stuff away, if any
 | ||||||
|         if (this.geoLayer !== undefined) { |         if (this.geoLayer !== undefined) { | ||||||
|  | @ -189,7 +215,7 @@ export default class ShowDataLayer { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (options.zoomToFeatures ?? false) { |         if (options.zoomToFeatures ?? false) { | ||||||
|             if(this.geoLayer.getLayers().length > 0){ |             if (this.geoLayer.getLayers().length > 0) { | ||||||
|                 try { |                 try { | ||||||
|                     const bounds = this.geoLayer.getBounds() |                     const bounds = this.geoLayer.getBounds() | ||||||
|                     mp.fitBounds(bounds, {animate: false}) |                     mp.fitBounds(bounds, {animate: false}) | ||||||
|  | @ -203,6 +229,7 @@ export default class ShowDataLayer { | ||||||
|             mp.addLayer(this.geoLayer) |             mp.addLayer(this.geoLayer) | ||||||
|         } |         } | ||||||
|         this.isDirty = false; |         this.isDirty = false; | ||||||
|  |         this.openPopupOfSelectedElement(this._selectedElement?.data) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -250,7 +277,7 @@ export default class ShowDataLayer { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * POst processing - basically adding the popup |      * Post processing - basically adding the popup | ||||||
|      * @param feature |      * @param feature | ||||||
|      * @param leafletLayer |      * @param leafletLayer | ||||||
|      * @private |      * @private | ||||||
|  | @ -290,6 +317,10 @@ export default class ShowDataLayer { | ||||||
|             } |             } | ||||||
|             infobox.AttachTo(id) |             infobox.AttachTo(id) | ||||||
|             infobox.Activate(); |             infobox.Activate(); | ||||||
|  |             this.unregister.push(() => { | ||||||
|  |                 console.log("Destroying infobox") | ||||||
|  |                 infobox.Destroy(); | ||||||
|  |             }) | ||||||
|             if (this._selectedElement?.data?.properties?.id !== feature.properties.id) { |             if (this._selectedElement?.data?.properties?.id !== feature.properties.id) { | ||||||
|                 this._selectedElement?.setData(feature) |                 this._selectedElement?.setData(feature) | ||||||
|             } |             } | ||||||
|  | @ -303,7 +334,6 @@ export default class ShowDataLayer { | ||||||
|             leafletlayer: leafletLayer |             leafletlayer: leafletLayer | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | @ -34,6 +34,11 @@ export class Translation extends BaseUIElement { | ||||||
|         return this.textFor(Translation.forcedLanguage ?? Locale.language.data) |         return this.textFor(Translation.forcedLanguage ?? Locale.language.data) | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     Destroy() { | ||||||
|  |         super.Destroy(); | ||||||
|  |         this.isDestroyed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     static ExtractAllTranslationsFrom(object: any, context = ""): { context: string, tr: Translation }[] { |     static ExtractAllTranslationsFrom(object: any, context = ""): { context: string, tr: Translation }[] { | ||||||
|         const allTranslations: { context: string, tr: Translation }[] = [] |         const allTranslations: { context: string, tr: Translation }[] = [] | ||||||
|         for (const key in object) { |         for (const key in object) { | ||||||
|  | @ -90,7 +95,11 @@ export class Translation extends BaseUIElement { | ||||||
| 
 | 
 | ||||||
|     InnerConstructElement(): HTMLElement { |     InnerConstructElement(): HTMLElement { | ||||||
|         const el = document.createElement("span") |         const el = document.createElement("span") | ||||||
|  |         const self = this | ||||||
|         Locale.language.addCallbackAndRun(_ => { |         Locale.language.addCallbackAndRun(_ => { | ||||||
|  |             if(self.isDestroyed){ | ||||||
|  |                 return true | ||||||
|  |             } | ||||||
|             el.innerHTML = this.txt |             el.innerHTML = this.txt | ||||||
|         }) |         }) | ||||||
|         return el; |         return el; | ||||||
|  |  | ||||||
|  | @ -140,6 +140,7 @@ | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "id": "wikipedia", |       "id": "wikipedia", | ||||||
|  |       "#": "Note that this is a _read_only_ option, to prevent people entering a 'wikidata'-link instead of 'name:etymology:wikidata'", | ||||||
|       "render": { |       "render": { | ||||||
|         "en": "A Wikipedia article about this <b>street</b> exists:<br/>{wikipedia():max-height:25rem}" |         "en": "A Wikipedia article about this <b>street</b> exists:<br/>{wikipedia():max-height:25rem}" | ||||||
|       }, |       }, | ||||||
|  | @ -149,7 +150,7 @@ | ||||||
|   "mapRendering": [ |   "mapRendering": [ | ||||||
|     { |     { | ||||||
|       "icon": { |       "icon": { | ||||||
|         "render": "pin:#05d7fcaa;./assets/layers/etymology/logo.svg", |         "render": "pin:#05d7fcaa", | ||||||
|         "mappings": [ |         "mappings": [ | ||||||
|           { |           { | ||||||
|             "if": { |             "if": { | ||||||
|  | @ -158,7 +159,7 @@ | ||||||
|                 "name:etymology:wikidata=" |                 "name:etymology:wikidata=" | ||||||
|               ] |               ] | ||||||
|             }, |             }, | ||||||
|             "then": "pin:#fcca05aa;./assets/layers/etymology/logo.svg" |             "then": "pin:#fcca05aa" | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
|       }, |       }, | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ | ||||||
|     "de" |     "de" | ||||||
|   ], |   ], | ||||||
|   "maintainer": "", |   "maintainer": "", | ||||||
|   "icon": "./assets/svg/bug.svg", |   "icon": "./assets/themes/grb_import/grb.svg", | ||||||
|   "version": "0", |   "version": "0", | ||||||
|   "startLat": 51.0249, |   "startLat": 51.0249, | ||||||
|   "startLon": 4.026489, |   "startLon": 4.026489, | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue