From 62f280feae86719d938d7193f3e75d53da942a0e Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 4 Dec 2021 21:49:17 +0100 Subject: [PATCH] First version of automatic dependency injection --- Models/ThemeConfig/LayerConfig.ts | 63 ++++++++++++++++++++---------- Models/ThemeConfig/LayoutConfig.ts | 38 ++++++++++++++---- Models/ThemeConfig/PresetConfig.ts | 12 +++--- 3 files changed, 81 insertions(+), 32 deletions(-) diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index 34245bd7b7..1128ecb943 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -1,7 +1,7 @@ import {Translation} from "../../UI/i18n/Translation"; import SourceConfig from "./SourceConfig"; import TagRenderingConfig from "./TagRenderingConfig"; -import PresetConfig from "./PresetConfig"; +import PresetConfig, {PreciseInput} from "./PresetConfig"; import {LayerConfigJson} from "./Json/LayerConfigJson"; import Translations from "../../UI/i18n/Translations"; import {TagUtils} from "../../Logic/Tags/TagUtils"; @@ -26,19 +26,19 @@ import * as icons from "../../assets/tagRenderings/icons.json" export default class LayerConfig extends WithContextLoader { - id: string; - name: Translation; - description: Translation; - source: SourceConfig; - calculatedTags: [string, string][]; - doNotDownload: boolean; - passAllFeatures: boolean; - isShown: TagRenderingConfig; - minzoom: number; - minzoomVisible: number; - maxzoom: number; - title?: TagRenderingConfig; - titleIcons: TagRenderingConfig[]; + public readonly id: string; + public readonly name: Translation; + public readonly description: Translation; + public readonly source: SourceConfig; + public readonly calculatedTags: [string, string][]; + public readonly doNotDownload: boolean; + public readonly passAllFeatures: boolean; + public readonly isShown: TagRenderingConfig; + public readonly minzoom: number; + public readonly minzoomVisible: number; + public readonly maxzoom: number; + public readonly title?: TagRenderingConfig; + public readonly titleIcons: TagRenderingConfig[]; public readonly mapRendering: PointRenderingConfig[] public readonly lineRendering: LineRenderingConfig[] @@ -52,10 +52,10 @@ export default class LayerConfig extends WithContextLoader { */ public readonly maxAgeOfCache: number - presets: PresetConfig[]; + public readonly presets: PresetConfig[]; - tagRenderings: TagRenderingConfig[]; - filters: FilterConfig[]; + public readonly tagRenderings: TagRenderingConfig[]; + public readonly filters: FilterConfig[]; constructor( json: LayerConfigJson, @@ -151,8 +151,7 @@ export default class LayerConfig extends WithContextLoader { throw "Presets should be a list of items (at " + context + ")" } this.presets = (json.presets ?? []).map((pr, i) => { - - let preciseInput: any = { + let preciseInput: PreciseInput = { preferredBackground: ["photo"], snapToLayers: undefined, maxSnapDistance: undefined @@ -163,6 +162,7 @@ export default class LayerConfig extends WithContextLoader { preferredBackground: undefined } } + let snapToLayers: string[]; if (typeof pr.preciseInput.snapToLayer === "string") { snapToLayers = [pr.preciseInput.snapToLayer] @@ -178,7 +178,7 @@ export default class LayerConfig extends WithContextLoader { } preciseInput = { preferredBackground: preferredBackground, - snapToLayers: snapToLayers, + snapToLayers, maxSnapDistance: pr.preciseInput.maxSnapDistance ?? 10 } } @@ -439,6 +439,10 @@ export default class LayerConfig extends WithContextLoader { ] } + for (const dep of Array.from(this.getDependencies())) { + extraProps.push(new Combine(["This layer will automatically load ", new Link(dep, "#"+dep)," into the layout as it depends on it."])) + } + return new Combine([ new Title(this.id, 3), this.description, @@ -478,4 +482,23 @@ export default class LayerConfig extends WithContextLoader { public isLeftRightSensitive(): boolean { return this.lineRendering.some(lr => lr.leftRightSensitive) } + + /** + * Returns a set of all other layer-ids that this layer needs to function. + * E.g. if this layers does snap to another layer in the preset, this other layer id will be mentioned + */ + public getDependencies(): Set{ + const deps = new Set() + + for (const preset of this.presets ?? []) { + if(preset.preciseInput?.snapToLayers === undefined){ + continue + } + preset.preciseInput?.snapToLayers?.forEach(id => { + deps.add(id); + }) + } + + return deps + } } \ No newline at end of file diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index 61efd85203..09c71c8243 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -166,7 +166,7 @@ export default class LayoutConfig { return } else { const shared = AllKnownLayers.sharedLayers.get(layer) - if(shared === undefined){ + if (shared === undefined) { throw `Shared layer ${layer} not found (at ${context}.layers[${i}])` } result.push(shared) @@ -212,15 +212,39 @@ export default class LayoutConfig { }) }); - + // Some special layers which are always included by default for (const defaultLayer of AllKnownLayers.added_by_default) { - if(result.some(l => l?.id === defaultLayer)){ + if (result.some(l => l?.id === defaultLayer)) { continue; // Already added } - result.push(AllKnownLayers.sharedLayers.get(defaultLayer)) + const sharedLayer = AllKnownLayers.sharedLayers.get(defaultLayer) + if (sharedLayer !== undefined) { + result.push(sharedLayer) + } } + + let unmetDependencies: { dependency: string, layer: string }[] = [] + do { + const dependencies: { dependency: string, layer: string }[] = [].concat(...result.map(l => Array.from(l.getDependencies()).map(d => ({ + dependency: d, + layer: l.id + })))) + const loadedLayers = new Set(result.map(r => r.id)) + unmetDependencies = dependencies.filter(dep => !loadedLayers.has(dep.dependency)) + for (const unmetDependency of unmetDependencies) { + + console.log("Recursively loading unmet dependency ", unmetDependency.dependency, "(needed by " + unmetDependency.layer + ")") + const dep = AllKnownLayers.sharedLayers.get(unmetDependency.dependency) + if (dep === undefined) { + throw "The layer '" + unmetDependency.layer + "' needs '" + unmetDependency.dependency + "' to be loaded, but it could not be found as builtin layer (at " + context + ")" + } + result.unshift(dep) + unmetDependencies = unmetDependencies.filter(d => d.dependency !== unmetDependency.dependency) + } + + } while (unmetDependencies.length > 0) return {layers: result, extractAllNodes: exportAllNodes} } @@ -300,9 +324,9 @@ export default class LayoutConfig { public isLeftRightSensitive() { return this.layers.some(l => l.isLeftRightSensitive()) } - - public getMatchingLayer(tags: any) : LayerConfig | undefined{ - if(tags === undefined){ + + public getMatchingLayer(tags: any): LayerConfig | undefined { + if (tags === undefined) { return undefined } for (const layer of this.layers) { diff --git a/Models/ThemeConfig/PresetConfig.ts b/Models/ThemeConfig/PresetConfig.ts index 9f198289bb..da823fcd28 100644 --- a/Models/ThemeConfig/PresetConfig.ts +++ b/Models/ThemeConfig/PresetConfig.ts @@ -1,6 +1,12 @@ import {Translation} from "../../UI/i18n/Translation"; import {Tag} from "../../Logic/Tags/Tag"; +export interface PreciseInput { + preferredBackground?: string[], + snapToLayers?: string[], + maxSnapDistance?: number +} + export default interface PresetConfig { title: Translation, tags: Tag[], @@ -8,9 +14,5 @@ export default interface PresetConfig { /** * If precise input is set, then an extra map is shown in which the user can drag the map to the precise location */ - preciseInput?: { - preferredBackground?: string[], - snapToLayers?: string[], - maxSnapDistance?: number - } + preciseInput?: PreciseInput } \ No newline at end of file