import { SubstitutedTranslation } from "../../UI/SubstitutedTranslation" import TagRenderingConfig from "./TagRenderingConfig" import { ExtraFuncParams, ExtraFunctions } from "../../Logic/ExtraFunctions" import LayerConfig from "./LayerConfig" import { SpecialVisualization } from "../../UI/SpecialVisualization" export default class DependencyCalculator { public static GetTagRenderingDependencies(tr: TagRenderingConfig): string[] { if (tr === undefined) { throw "Got undefined tag rendering in getTagRenderingDependencies" } const deps: string[] = [] // All translated snippets const parts: string[] = [].concat(...tr.EnumerateTranslations().map((tr) => tr.AllValues())) for (const part of parts) { const specialVizs: { func: SpecialVisualization; args: string[] }[] = SubstitutedTranslation.ExtractSpecialComponents(part) .map((o) => o.special) .filter((o) => o?.func?.getLayerDependencies !== undefined) for (const specialViz of specialVizs) { deps.push(...specialViz.func.getLayerDependencies(specialViz.args)) } } return deps } /** * 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 static getLayerDependencies( layer: LayerConfig ): { neededLayer: string; reason: string; context?: string; neededBy: string }[] { const deps: { neededLayer: string; reason: string; context?: string; neededBy: string }[] = [] for (let i = 0; layer.presets !== undefined && i < layer.presets.length; i++) { const preset = layer.presets[i] preset.preciseInput?.snapToLayers?.forEach((id) => { deps.push({ neededLayer: id, reason: "a preset snaps to this layer", context: "presets[" + i + "]", neededBy: layer.id, }) }) } for (const tr of layer.AllTagRenderings()) { for (const dep of DependencyCalculator.GetTagRenderingDependencies(tr)) { deps.push({ neededLayer: dep, reason: "a tagrendering needs this layer", context: tr.id, neededBy: layer.id, }) } } if (layer.calculatedTags?.length > 0) { const obj = { type: "Feature", geometry: { type: "Point", coordinates: [0, 0], }, properties: { id: "node/1", }, } let currentKey = undefined let currentLine = undefined const params: ExtraFuncParams = { getFeatureById: (_) => undefined, getFeaturesWithin: (layerId, _) => { if (layerId === "*") { // This is a wildcard return [] } // The important line: steal the dependencies! deps.push({ neededLayer: layerId, reason: "a calculated tag loads features from this layer", context: "calculatedTag[" + currentLine + "] which calculates the value for " + currentKey, neededBy: layer.id, }) return [] }, memberships: undefined, } // Init the extra patched functions... ExtraFunctions.FullPatchFeature(params, obj) // ... Run the calculated tag code, which will trigger the getFeaturesWithin above... for (let i = 0; i < layer.calculatedTags.length; i++) { const [key, code] = layer.calculatedTags[i] currentLine = i // Leak the state... currentKey = key try { const func = new Function("feat", "return " + code + ";") const result = func(obj) obj.properties[key] = JSON.stringify(result) } catch (e) {} } } return deps } }