forked from MapComplete/MapComplete
		
	Refactoring: split rendering of centroid to PointRendering
This commit is contained in:
		
							parent
							
								
									ad8674dd70
								
							
						
					
					
						commit
						584ade8e61
					
				
					 8 changed files with 509 additions and 440 deletions
				
			
		| 
						 | 
					@ -4,6 +4,7 @@ import FilterConfigJson from "./FilterConfigJson";
 | 
				
			||||||
import {DeleteConfigJson} from "./DeleteConfigJson";
 | 
					import {DeleteConfigJson} from "./DeleteConfigJson";
 | 
				
			||||||
import UnitConfigJson from "./UnitConfigJson";
 | 
					import UnitConfigJson from "./UnitConfigJson";
 | 
				
			||||||
import MoveConfigJson from "./MoveConfigJson";
 | 
					import MoveConfigJson from "./MoveConfigJson";
 | 
				
			||||||
 | 
					import PointRenderingConfigJson from "./PointRenderingConfigJson";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Configuration for a single layer
 | 
					 * Configuration for a single layer
 | 
				
			||||||
| 
						 | 
					@ -119,47 +120,8 @@ export interface LayerConfigJson {
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    titleIcons?: (string | TagRenderingConfigJson)[];
 | 
					    titleIcons?: (string | TagRenderingConfigJson)[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * The icon for an element.
 | 
					 | 
				
			||||||
     * Note that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * The result of the icon is rendered as follows:
 | 
					 | 
				
			||||||
     * the resulting string is interpreted as a _list_ of items, separated by ";". The bottommost layer is the first layer.
 | 
					 | 
				
			||||||
     * As a result, on could use a generic pin, then overlay it with a specific icon.
 | 
					 | 
				
			||||||
     * To make things even more practical, one can use all SVG's from the folder "assets/svg" and _substitute the color_ in it.
 | 
					 | 
				
			||||||
     * E.g. to draw a red pin, use "pin:#f00", to have a green circle with your icon on top, use `circle:#0f0;<path to my icon.svg>`
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    icon?: string | TagRenderingConfigJson;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    mapRendering: PointRenderingConfigJson[]
 | 
				
			||||||
     * IconsOverlays are a list of extra icons/badges to overlay over the icon.
 | 
					 | 
				
			||||||
     * The 'badge'-toggle changes their behaviour.
 | 
					 | 
				
			||||||
     * If badge is set, it will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout.
 | 
					 | 
				
			||||||
     * If badges is false, it'll be a simple overlay
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * Note: strings are interpreted as icons, so layering and substituting is supported
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    iconOverlays?: { if: string | AndOrTagConfigJson, then: string | TagRenderingConfigJson, badge?: boolean }[]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * A string containing "width,height" or "width,height,anchorpoint" where anchorpoint is any of 'center', 'top', 'bottom', 'left', 'right', 'bottomleft','topright', ...
 | 
					 | 
				
			||||||
     * Default is '40,40,center'
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    iconSize?: string | TagRenderingConfigJson;
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * The rotation of an icon, useful for e.g. directions.
 | 
					 | 
				
			||||||
     * Usage: as if it were a css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)``
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    rotation?: string | TagRenderingConfigJson;
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * A HTML-fragment that is shown below the icon, for example:
 | 
					 | 
				
			||||||
     * <div style="background: white; display: block">{name}</div>
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * If the icon is undefined, then the label is shown in the center of the feature.
 | 
					 | 
				
			||||||
     * Note that, if the wayhandling hides the icon then no label is shown as well.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    label?: string | TagRenderingConfigJson;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * The color for way-elements and SVG-elements.
 | 
					     * The color for way-elements and SVG-elements.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										62
									
								
								Models/ThemeConfig/Json/PointRenderingConfigJson.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								Models/ThemeConfig/Json/PointRenderingConfigJson.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,62 @@
 | 
				
			||||||
 | 
					import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
 | 
				
			||||||
 | 
					import {AndOrTagConfigJson} from "./TagConfigJson";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The PointRenderingConfig gives all details onto how to render a single point of a feature.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * This can be used if:
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * - The feature is a point
 | 
				
			||||||
 | 
					 * - To render something at the centroid of an area, or at the start, end or projected centroid of a way
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export default interface PointRenderingConfigJson {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * All the locations that this point should be rendered at.
 | 
				
			||||||
 | 
					     * Using `location: ["point", "centroid"] will always render centerpoint
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    location: ("point" | "centroid")[]
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The icon for an element.
 | 
				
			||||||
 | 
					     * Note that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * The result of the icon is rendered as follows:
 | 
				
			||||||
 | 
					     * the resulting string is interpreted as a _list_ of items, separated by ";". The bottommost layer is the first layer.
 | 
				
			||||||
 | 
					     * As a result, on could use a generic pin, then overlay it with a specific icon.
 | 
				
			||||||
 | 
					     * To make things even more practical, one can use all SVG's from the folder "assets/svg" and _substitute the color_ in it.
 | 
				
			||||||
 | 
					     * E.g. to draw a red pin, use "pin:#f00", to have a green circle with your icon on top, use `circle:#0f0;<path to my icon.svg>`
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    icon?: string | TagRenderingConfigJson;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * IconsOverlays are a list of extra icons/badges to overlay over the icon.
 | 
				
			||||||
 | 
					     * The 'badge'-toggle changes their behaviour.
 | 
				
			||||||
 | 
					     * If badge is set, it will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout.
 | 
				
			||||||
 | 
					     * If badges is false, it'll be a simple overlay
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Note: strings are interpreted as icons, so layering and substituting is supported
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    iconOverlays?: { if: string | AndOrTagConfigJson, then: string | TagRenderingConfigJson, badge?: boolean }[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * A string containing "width,height" or "width,height,anchorpoint" where anchorpoint is any of 'center', 'top', 'bottom', 'left', 'right', 'bottomleft','topright', ...
 | 
				
			||||||
 | 
					     * Default is '40,40,center'
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    iconSize?: string | TagRenderingConfigJson;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The rotation of an icon, useful for e.g. directions.
 | 
				
			||||||
 | 
					     * Usage: as if it were a css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)``
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    rotation?: string | TagRenderingConfigJson;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * A HTML-fragment that is shown below the icon, for example:
 | 
				
			||||||
 | 
					     * <div style="background: white; display: block">{name}</div>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * If the icon is undefined, then the label is shown in the center of the feature.
 | 
				
			||||||
 | 
					     * Note that, if the wayhandling hides the icon then no label is shown as well.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    label?: string | TagRenderingConfigJson;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -6,22 +6,17 @@ import PresetConfig from "./PresetConfig";
 | 
				
			||||||
import {LayerConfigJson} from "./Json/LayerConfigJson";
 | 
					import {LayerConfigJson} from "./Json/LayerConfigJson";
 | 
				
			||||||
import Translations from "../../UI/i18n/Translations";
 | 
					import Translations from "../../UI/i18n/Translations";
 | 
				
			||||||
import {TagUtils} from "../../Logic/Tags/TagUtils";
 | 
					import {TagUtils} from "../../Logic/Tags/TagUtils";
 | 
				
			||||||
import SharedTagRenderings from "../../Customizations/SharedTagRenderings";
 | 
					 | 
				
			||||||
import {TagRenderingConfigJson} from "./Json/TagRenderingConfigJson";
 | 
					 | 
				
			||||||
import {Utils} from "../../Utils";
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
import {UIEventSource} from "../../Logic/UIEventSource";
 | 
					import {UIEventSource} from "../../Logic/UIEventSource";
 | 
				
			||||||
import BaseUIElement from "../../UI/BaseUIElement";
 | 
					import BaseUIElement from "../../UI/BaseUIElement";
 | 
				
			||||||
import {FixedUiElement} from "../../UI/Base/FixedUiElement";
 | 
					 | 
				
			||||||
import Combine from "../../UI/Base/Combine";
 | 
					 | 
				
			||||||
import {VariableUiElement} from "../../UI/Base/VariableUIElement";
 | 
					 | 
				
			||||||
import FilterConfig from "./FilterConfig";
 | 
					import FilterConfig from "./FilterConfig";
 | 
				
			||||||
import {Unit} from "../Unit";
 | 
					import {Unit} from "../Unit";
 | 
				
			||||||
import DeleteConfig from "./DeleteConfig";
 | 
					import DeleteConfig from "./DeleteConfig";
 | 
				
			||||||
import Svg from "../../Svg";
 | 
					 | 
				
			||||||
import Img from "../../UI/Base/Img";
 | 
					 | 
				
			||||||
import MoveConfig from "./MoveConfig";
 | 
					import MoveConfig from "./MoveConfig";
 | 
				
			||||||
 | 
					import PointRenderingConfig from "./PointRenderingConfig";
 | 
				
			||||||
 | 
					import WithContextLoader from "./WithContextLoader";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class LayerConfig {
 | 
					export default class LayerConfig extends WithContextLoader{
 | 
				
			||||||
    static WAYHANDLING_DEFAULT = 0;
 | 
					    static WAYHANDLING_DEFAULT = 0;
 | 
				
			||||||
    static WAYHANDLING_CENTER_ONLY = 1;
 | 
					    static WAYHANDLING_CENTER_ONLY = 1;
 | 
				
			||||||
    static WAYHANDLING_CENTER_AND_WAY = 2;
 | 
					    static WAYHANDLING_CENTER_AND_WAY = 2;
 | 
				
			||||||
| 
						 | 
					@ -39,11 +34,9 @@ export default class LayerConfig {
 | 
				
			||||||
    maxzoom: number;
 | 
					    maxzoom: number;
 | 
				
			||||||
    title?: TagRenderingConfig;
 | 
					    title?: TagRenderingConfig;
 | 
				
			||||||
    titleIcons: TagRenderingConfig[];
 | 
					    titleIcons: TagRenderingConfig[];
 | 
				
			||||||
    icon: TagRenderingConfig;
 | 
					    
 | 
				
			||||||
    iconOverlays: { if: TagsFilter; then: TagRenderingConfig; badge: boolean }[];
 | 
					    public readonly mapRendering: PointRenderingConfig[]
 | 
				
			||||||
    iconSize: TagRenderingConfig;
 | 
					
 | 
				
			||||||
    label: TagRenderingConfig;
 | 
					 | 
				
			||||||
    rotation: TagRenderingConfig;
 | 
					 | 
				
			||||||
    color: TagRenderingConfig;
 | 
					    color: TagRenderingConfig;
 | 
				
			||||||
    width: TagRenderingConfig;
 | 
					    width: TagRenderingConfig;
 | 
				
			||||||
    dashArray: TagRenderingConfig;
 | 
					    dashArray: TagRenderingConfig;
 | 
				
			||||||
| 
						 | 
					@ -63,25 +56,9 @@ export default class LayerConfig {
 | 
				
			||||||
        context?: string,
 | 
					        context?: string,
 | 
				
			||||||
        official: boolean = true
 | 
					        official: boolean = true
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
        context = context + "." + json.id;
 | 
					        context = context + "." + json.id;
 | 
				
			||||||
        const self = this;
 | 
					        super(json, context)
 | 
				
			||||||
        this.id = json.id;
 | 
					        this.id = json.id;
 | 
				
			||||||
        this.allowSplit = json.allowSplit ?? false;
 | 
					 | 
				
			||||||
        this.name = Translations.T(json.name, context + ".name");
 | 
					 | 
				
			||||||
        this.units = (json.units ?? []).map(((unitJson, i) => Unit.fromJson(unitJson, `${context}.unit[${i}]`)))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (json.description !== undefined) {
 | 
					 | 
				
			||||||
            if (Object.keys(json.description).length === 0) {
 | 
					 | 
				
			||||||
                json.description = undefined;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.description = Translations.T(
 | 
					 | 
				
			||||||
            json.description,
 | 
					 | 
				
			||||||
            context + ".description"
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let legacy = undefined;
 | 
					        let legacy = undefined;
 | 
				
			||||||
        if (json["overpassTags"] !== undefined) {
 | 
					        if (json["overpassTags"] !== undefined) {
 | 
				
			||||||
            // @ts-ignore
 | 
					            // @ts-ignore
 | 
				
			||||||
| 
						 | 
					@ -111,7 +88,7 @@ export default class LayerConfig {
 | 
				
			||||||
                throw context + "Use 'geoJson' instead of 'geojson' (the J is a capital letter)";
 | 
					                throw context + "Use 'geoJson' instead of 'geojson' (the J is a capital letter)";
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.source = new SourceConfig(
 | 
					           this. source = new SourceConfig(
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    osmTags: osmTags,
 | 
					                    osmTags: osmTags,
 | 
				
			||||||
                    geojsonSource: json.source["geoJson"],
 | 
					                    geojsonSource: json.source["geoJson"],
 | 
				
			||||||
| 
						 | 
					@ -119,14 +96,33 @@ export default class LayerConfig {
 | 
				
			||||||
                    overpassScript: json.source["overpassScript"],
 | 
					                    overpassScript: json.source["overpassScript"],
 | 
				
			||||||
                    isOsmCache: json.source["isOsmCache"],
 | 
					                    isOsmCache: json.source["isOsmCache"],
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                this.id
 | 
					                json.id
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            this.source = new SourceConfig({
 | 
					           this. source = new SourceConfig({
 | 
				
			||||||
                osmTags: legacy,
 | 
					                osmTags: legacy,
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       
 | 
				
			||||||
 | 
					        this.id = json.id;
 | 
				
			||||||
 | 
					        this.allowSplit = json.allowSplit ?? false;
 | 
				
			||||||
 | 
					        this.name = Translations.T(json.name, context + ".name");
 | 
				
			||||||
 | 
					        this.units = (json.units ?? []).map(((unitJson, i) => Unit.fromJson(unitJson, `${context}.unit[${i}]`)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (json.description !== undefined) {
 | 
				
			||||||
 | 
					            if (Object.keys(json.description).length === 0) {
 | 
				
			||||||
 | 
					                json.description = undefined;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.description = Translations.T(
 | 
				
			||||||
 | 
					            json.description,
 | 
				
			||||||
 | 
					            context + ".description"
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.calculatedTags = undefined;
 | 
					        this.calculatedTags = undefined;
 | 
				
			||||||
        if (json.calculatedTags !== undefined) {
 | 
					        if (json.calculatedTags !== undefined) {
 | 
				
			||||||
            if (!official) {
 | 
					            if (!official) {
 | 
				
			||||||
| 
						 | 
					@ -202,101 +198,15 @@ export default class LayerConfig {
 | 
				
			||||||
            return config;
 | 
					            return config;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /** Given a key, gets the corresponding property from the json (or the default if not found
 | 
					      
 | 
				
			||||||
         *
 | 
					
 | 
				
			||||||
         * The found value is interpreted as a tagrendering and fetched/parsed
 | 
					        this.mapRendering = json.mapRendering.map((r, i) => new PointRenderingConfig(r, context+".mapRendering["+i+"]"))
 | 
				
			||||||
         * */
 | 
					      
 | 
				
			||||||
        function tr(key: string, deflt) {
 | 
					        if(this.mapRendering.length > 1){
 | 
				
			||||||
            const v = json[key];
 | 
					            throw "Invalid maprendering for "+this.id+", currently only one mapRendering is supported!"
 | 
				
			||||||
            if (v === undefined || v === null) {
 | 
					 | 
				
			||||||
                if (deflt === undefined) {
 | 
					 | 
				
			||||||
                    return undefined;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                return new TagRenderingConfig(
 | 
					 | 
				
			||||||
                    deflt,
 | 
					 | 
				
			||||||
                    self.source.osmTags,
 | 
					 | 
				
			||||||
                    `${context}.${key}.default value`
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (typeof v === "string") {
 | 
					 | 
				
			||||||
                const shared = SharedTagRenderings.SharedTagRendering.get(v);
 | 
					 | 
				
			||||||
                if (shared) {
 | 
					 | 
				
			||||||
                    return shared;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return new TagRenderingConfig(
 | 
					 | 
				
			||||||
                v,
 | 
					 | 
				
			||||||
                self.source.osmTags,
 | 
					 | 
				
			||||||
                `${context}.${key}`
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					        this.tagRenderings = this.trs(json.tagRenderings, false);
 | 
				
			||||||
         * Converts a list of tagRenderingCOnfigJSON in to TagRenderingConfig
 | 
					 | 
				
			||||||
         * A string is interpreted as a name to call
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        function trs(
 | 
					 | 
				
			||||||
            tagRenderings?: (string | { builtin: string, override: any } | TagRenderingConfigJson)[],
 | 
					 | 
				
			||||||
            readOnly = false
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
            if (tagRenderings === undefined) {
 | 
					 | 
				
			||||||
                return [];
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return Utils.NoNull(
 | 
					 | 
				
			||||||
                tagRenderings.map((renderingJson, i) => {
 | 
					 | 
				
			||||||
                    if (typeof renderingJson === "string") {
 | 
					 | 
				
			||||||
                        renderingJson = {builtin: renderingJson, override: undefined}
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (renderingJson["builtin"] !== undefined) {
 | 
					 | 
				
			||||||
                        const renderingId = renderingJson["builtin"]
 | 
					 | 
				
			||||||
                        if (renderingId === "questions") {
 | 
					 | 
				
			||||||
                            if (readOnly) {
 | 
					 | 
				
			||||||
                                throw `A tagrendering has a question, but asking a question does not make sense here: is it a title icon or a geojson-layer? ${context}. The offending tagrendering is ${JSON.stringify(
 | 
					 | 
				
			||||||
                                    renderingJson
 | 
					 | 
				
			||||||
                                )}`;
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            return new TagRenderingConfig("questions", undefined, context);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if (renderingJson["override"] !== undefined) {
 | 
					 | 
				
			||||||
                            const sharedJson = SharedTagRenderings.SharedTagRenderingJson.get(renderingId)
 | 
					 | 
				
			||||||
                            return new TagRenderingConfig(
 | 
					 | 
				
			||||||
                                Utils.Merge(renderingJson["override"], sharedJson),
 | 
					 | 
				
			||||||
                                self.source.osmTags,
 | 
					 | 
				
			||||||
                                `${context}.tagrendering[${i}]+override`
 | 
					 | 
				
			||||||
                            );
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        const shared = SharedTagRenderings.SharedTagRendering.get(renderingId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if (shared !== undefined) {
 | 
					 | 
				
			||||||
                            return shared;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        if (Utils.runningFromConsole) {
 | 
					 | 
				
			||||||
                            return undefined;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        const keys = Array.from(
 | 
					 | 
				
			||||||
                            SharedTagRenderings.SharedTagRendering.keys()
 | 
					 | 
				
			||||||
                        );
 | 
					 | 
				
			||||||
                        throw `Predefined tagRendering ${renderingId} not found in ${context}.\n    Try one of ${keys.join(
 | 
					 | 
				
			||||||
                            ", "
 | 
					 | 
				
			||||||
                        )}\n    If you intent to output this text literally, use {\"render\": <your text>} instead"}`;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    return new TagRenderingConfig(
 | 
					 | 
				
			||||||
                        <TagRenderingConfigJson>renderingJson,
 | 
					 | 
				
			||||||
                        self.source.osmTags,
 | 
					 | 
				
			||||||
                        `${context}.tagrendering[${i}]`
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.tagRenderings = trs(json.tagRenderings, false);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const missingIds = json.tagRenderings?.filter(tr => typeof tr !== "string" && tr["builtin"] === undefined && tr["id"] === undefined) ?? [];
 | 
					        const missingIds = json.tagRenderings?.filter(tr => typeof tr !== "string" && tr["builtin"] === undefined && tr["id"] === undefined) ?? [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -329,43 +239,13 @@ export default class LayerConfig {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.titleIcons = trs(titleIcons, true);
 | 
					        this.titleIcons = this.trs(titleIcons, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.title = tr("title", undefined);
 | 
					        this.title = this.tr("title", undefined);
 | 
				
			||||||
        this.icon = tr("icon", "");
 | 
					        this.isShown = this.tr("isShown", "yes");
 | 
				
			||||||
        this.iconOverlays = (json.iconOverlays ?? []).map((overlay, i) => {
 | 
					        this.color = this.tr("color", "#0000ff");
 | 
				
			||||||
            let tr = new TagRenderingConfig(
 | 
					        this.width = this.tr("width", "7");
 | 
				
			||||||
                overlay.then,
 | 
					        this.dashArray = this.tr("dashArray", "");
 | 
				
			||||||
                self.source.osmTags,
 | 
					 | 
				
			||||||
                `iconoverlays.${i}`
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            if (
 | 
					 | 
				
			||||||
                typeof overlay.then === "string" &&
 | 
					 | 
				
			||||||
                SharedTagRenderings.SharedIcons.get(overlay.then) !== undefined
 | 
					 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                tr = SharedTagRenderings.SharedIcons.get(overlay.then);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return {
 | 
					 | 
				
			||||||
                if: TagUtils.Tag(overlay.if),
 | 
					 | 
				
			||||||
                then: tr,
 | 
					 | 
				
			||||||
                badge: overlay.badge ?? false,
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const iconPath = this.icon.GetRenderValue({id: "node/-1"}).txt;
 | 
					 | 
				
			||||||
        if (iconPath.startsWith(Utils.assets_path)) {
 | 
					 | 
				
			||||||
            const iconKey = iconPath.substr(Utils.assets_path.length);
 | 
					 | 
				
			||||||
            if (Svg.All[iconKey] === undefined) {
 | 
					 | 
				
			||||||
                throw "Builtin SVG asset not found: " + iconPath;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        this.isShown = tr("isShown", "yes");
 | 
					 | 
				
			||||||
        this.iconSize = tr("iconSize", "40,40,center");
 | 
					 | 
				
			||||||
        this.label = tr("label", "");
 | 
					 | 
				
			||||||
        this.color = tr("color", "#0000ff");
 | 
					 | 
				
			||||||
        this.width = tr("width", "7");
 | 
					 | 
				
			||||||
        this.rotation = tr("rotation", "0");
 | 
					 | 
				
			||||||
        this.dashArray = tr("dashArray", "");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.deletion = null;
 | 
					        this.deletion = null;
 | 
				
			||||||
        if (json.deletion === true) {
 | 
					        if (json.deletion === true) {
 | 
				
			||||||
| 
						 | 
					@ -398,54 +278,9 @@ export default class LayerConfig {
 | 
				
			||||||
        if (this.calculatedTags === undefined) {
 | 
					        if (this.calculatedTags === undefined) {
 | 
				
			||||||
            return [];
 | 
					            return [];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        return this.calculatedTags.map((code) => code[1]);
 | 
					        return this.calculatedTags.map((code) => code[1]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public AddRoamingRenderings(addAll: {
 | 
					 | 
				
			||||||
        tagRenderings: TagRenderingConfig[];
 | 
					 | 
				
			||||||
        titleIcons: TagRenderingConfig[];
 | 
					 | 
				
			||||||
        iconOverlays: {
 | 
					 | 
				
			||||||
            if: TagsFilter;
 | 
					 | 
				
			||||||
            then: TagRenderingConfig;
 | 
					 | 
				
			||||||
            badge: boolean;
 | 
					 | 
				
			||||||
        }[];
 | 
					 | 
				
			||||||
    }): LayerConfig {
 | 
					 | 
				
			||||||
        let insertionPoint = this.tagRenderings
 | 
					 | 
				
			||||||
            .map((tr) => tr.IsQuestionBoxElement())
 | 
					 | 
				
			||||||
            .indexOf(true);
 | 
					 | 
				
			||||||
        if (insertionPoint < 0) {
 | 
					 | 
				
			||||||
            // No 'questions' defined - we just add them all to the end
 | 
					 | 
				
			||||||
            insertionPoint = this.tagRenderings.length;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        this.tagRenderings.splice(insertionPoint, 0, ...addAll.tagRenderings);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.iconOverlays.push(...addAll.iconOverlays);
 | 
					 | 
				
			||||||
        for (const icon of addAll.titleIcons) {
 | 
					 | 
				
			||||||
            this.titleIcons.splice(0, 0, icon);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return this;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public GetRoamingRenderings(): {
 | 
					 | 
				
			||||||
        tagRenderings: TagRenderingConfig[];
 | 
					 | 
				
			||||||
        titleIcons: TagRenderingConfig[];
 | 
					 | 
				
			||||||
        iconOverlays: {
 | 
					 | 
				
			||||||
            if: TagsFilter;
 | 
					 | 
				
			||||||
            then: TagRenderingConfig;
 | 
					 | 
				
			||||||
            badge: boolean;
 | 
					 | 
				
			||||||
        }[];
 | 
					 | 
				
			||||||
    } {
 | 
					 | 
				
			||||||
        const tagRenderings = this.tagRenderings.filter((tr) => tr.roaming);
 | 
					 | 
				
			||||||
        const titleIcons = this.titleIcons.filter((tr) => tr.roaming);
 | 
					 | 
				
			||||||
        const iconOverlays = this.iconOverlays.filter((io) => io.then.roaming);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
            tagRenderings: tagRenderings,
 | 
					 | 
				
			||||||
            titleIcons: titleIcons,
 | 
					 | 
				
			||||||
            iconOverlays: iconOverlays,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public GenerateLeafletStyle(
 | 
					    public GenerateLeafletStyle(
 | 
				
			||||||
        tags: UIEventSource<any>,
 | 
					        tags: UIEventSource<any>,
 | 
				
			||||||
| 
						 | 
					@ -463,13 +298,6 @@ export default class LayerConfig {
 | 
				
			||||||
        weight: number;
 | 
					        weight: number;
 | 
				
			||||||
        dashArray: number[];
 | 
					        dashArray: number[];
 | 
				
			||||||
    } {
 | 
					    } {
 | 
				
			||||||
        function num(str, deflt = 40) {
 | 
					 | 
				
			||||||
            const n = Number(str);
 | 
					 | 
				
			||||||
            if (isNaN(n)) {
 | 
					 | 
				
			||||||
                return deflt;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return n;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function rendernum(tr: TagRenderingConfig, deflt: number) {
 | 
					        function rendernum(tr: TagRenderingConfig, deflt: number) {
 | 
				
			||||||
            const str = Number(render(tr, "" + deflt));
 | 
					            const str = Number(render(tr, "" + deflt));
 | 
				
			||||||
| 
						 | 
					@ -488,7 +316,6 @@ export default class LayerConfig {
 | 
				
			||||||
            return Utils.SubstituteKeys(str, tags.data).replace(/{.*}/g, "");
 | 
					            return Utils.SubstituteKeys(str, tags.data).replace(/{.*}/g, "");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const iconSize = render(this.iconSize, "40,40,center").split(",");
 | 
					 | 
				
			||||||
        const dashArray = render(this.dashArray)?.split(" ")?.map(Number);
 | 
					        const dashArray = render(this.dashArray)?.split(" ")?.map(Number);
 | 
				
			||||||
        let color = render(this.color, "#00f");
 | 
					        let color = render(this.color, "#00f");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -500,134 +327,10 @@ export default class LayerConfig {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const weight = rendernum(this.width, 5);
 | 
					        const weight = rendernum(this.width, 5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const iconW = num(iconSize[0]);
 | 
					        const icon = this.mapRendering[0].GenerateLeafletStyle(tags, clickable)
 | 
				
			||||||
        let iconH = num(iconSize[1]);
 | 
					 | 
				
			||||||
        const mode = iconSize[2]?.trim()?.toLowerCase() ?? "center";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let anchorW = iconW / 2;
 | 
					 | 
				
			||||||
        let anchorH = iconH / 2;
 | 
					 | 
				
			||||||
        if (mode === "left") {
 | 
					 | 
				
			||||||
            anchorW = 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (mode === "right") {
 | 
					 | 
				
			||||||
            anchorW = iconW;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (mode === "top") {
 | 
					 | 
				
			||||||
            anchorH = 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (mode === "bottom") {
 | 
					 | 
				
			||||||
            anchorH = iconH;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const iconUrlStatic = render(this.icon);
 | 
					 | 
				
			||||||
        const self = this;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        function genHtmlFromString(sourcePart: string, rotation: string): BaseUIElement {
 | 
					 | 
				
			||||||
            const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`;
 | 
					 | 
				
			||||||
            let html: BaseUIElement = new FixedUiElement(
 | 
					 | 
				
			||||||
                `<img src="${sourcePart}" style="${style}" />`
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            const match = sourcePart.match(/([a-zA-Z0-9_]*):([^;]*)/);
 | 
					 | 
				
			||||||
            if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) {
 | 
					 | 
				
			||||||
                html = new Img(
 | 
					 | 
				
			||||||
                    (Svg.All[match[1] + ".svg"] as string).replace(
 | 
					 | 
				
			||||||
                        /#000000/g,
 | 
					 | 
				
			||||||
                        match[2]
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                    true
 | 
					 | 
				
			||||||
                ).SetStyle(style);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return html;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const mappedHtml = tags?.map((tgs) => {
 | 
					 | 
				
			||||||
            // What do you mean, 'tgs' is never read?
 | 
					 | 
				
			||||||
            // It is read implicitly in the 'render' method
 | 
					 | 
				
			||||||
            const iconUrl = render(self.icon);
 | 
					 | 
				
			||||||
            const rotation = render(self.rotation, "0deg");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            let htmlParts: BaseUIElement[] = [];
 | 
					 | 
				
			||||||
            let sourceParts = Utils.NoNull(
 | 
					 | 
				
			||||||
                iconUrl.split(";").filter((prt) => prt != "")
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            for (const sourcePart of sourceParts) {
 | 
					 | 
				
			||||||
                htmlParts.push(genHtmlFromString(sourcePart, rotation));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            let badges = [];
 | 
					 | 
				
			||||||
            for (const iconOverlay of self.iconOverlays) {
 | 
					 | 
				
			||||||
                if (!iconOverlay.if.matchesProperties(tgs)) {
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (iconOverlay.badge) {
 | 
					 | 
				
			||||||
                    const badgeParts: BaseUIElement[] = [];
 | 
					 | 
				
			||||||
                    const renderValue = iconOverlay
 | 
					 | 
				
			||||||
                        .then
 | 
					 | 
				
			||||||
                        .GetRenderValue(tgs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (renderValue === undefined) {
 | 
					 | 
				
			||||||
                        continue;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    const partDefs = renderValue.txt.split(";")
 | 
					 | 
				
			||||||
                        .filter((prt) => prt != "");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    for (const badgePartStr of partDefs) {
 | 
					 | 
				
			||||||
                        badgeParts.push(genHtmlFromString(badgePartStr, "0"));
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    const badgeCompound = new Combine(badgeParts).SetStyle(
 | 
					 | 
				
			||||||
                        "display:flex;position:relative;width:100%;height:100%;"
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    badges.push(badgeCompound);
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    htmlParts.push(
 | 
					 | 
				
			||||||
                        genHtmlFromString(iconOverlay.then.GetRenderValue(tgs).txt, "0")
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (badges.length > 0) {
 | 
					 | 
				
			||||||
                const badgesComponent = new Combine(badges).SetStyle(
 | 
					 | 
				
			||||||
                    "display:flex;height:50%;width:100%;position:absolute;top:50%;left:50%;"
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                htmlParts.push(badgesComponent);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (sourceParts.length == 0) {
 | 
					 | 
				
			||||||
                iconH = 0;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                const label = self.label
 | 
					 | 
				
			||||||
                    ?.GetRenderValue(tgs)
 | 
					 | 
				
			||||||
                    ?.Subs(tgs)
 | 
					 | 
				
			||||||
                    ?.SetClass("block text-center")
 | 
					 | 
				
			||||||
                    ?.SetStyle("margin-top: " + (iconH + 2) + "px");
 | 
					 | 
				
			||||||
                if (label !== undefined) {
 | 
					 | 
				
			||||||
                    htmlParts.push(
 | 
					 | 
				
			||||||
                        new Combine([label]).SetClass("flex flex-col items-center")
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } catch (e) {
 | 
					 | 
				
			||||||
                console.error(e, tgs);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return new Combine(htmlParts);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            icon: {
 | 
					            icon,
 | 
				
			||||||
                html: mappedHtml === undefined ? new FixedUiElement(self.icon.render.txt) : new VariableUiElement(mappedHtml),
 | 
					 | 
				
			||||||
                iconSize: [iconW, iconH],
 | 
					 | 
				
			||||||
                iconAnchor: [anchorW, anchorH],
 | 
					 | 
				
			||||||
                popupAnchor: [0, 3 - anchorH],
 | 
					 | 
				
			||||||
                iconUrl: iconUrlStatic,
 | 
					 | 
				
			||||||
                className: clickable
 | 
					 | 
				
			||||||
                    ? "leaflet-div-icon"
 | 
					 | 
				
			||||||
                    : "leaflet-div-icon unclickable",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            color: color,
 | 
					            color: color,
 | 
				
			||||||
            weight: weight,
 | 
					            weight: weight,
 | 
				
			||||||
            dashArray: dashArray,
 | 
					            dashArray: dashArray,
 | 
				
			||||||
| 
						 | 
					@ -638,19 +341,18 @@ export default class LayerConfig {
 | 
				
			||||||
        const parts: Set<string>[] = [];
 | 
					        const parts: Set<string>[] = [];
 | 
				
			||||||
        parts.push(...this.tagRenderings?.map((tr) => tr.ExtractImages(false)));
 | 
					        parts.push(...this.tagRenderings?.map((tr) => tr.ExtractImages(false)));
 | 
				
			||||||
        parts.push(...this.titleIcons?.map((tr) => tr.ExtractImages(true)));
 | 
					        parts.push(...this.titleIcons?.map((tr) => tr.ExtractImages(true)));
 | 
				
			||||||
        parts.push(this.icon?.ExtractImages(true));
 | 
					 | 
				
			||||||
        parts.push(
 | 
					 | 
				
			||||||
            ...this.iconOverlays?.map((overlay) => overlay.then.ExtractImages(true))
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        for (const preset of this.presets) {
 | 
					        for (const preset of this.presets) {
 | 
				
			||||||
            parts.push(new Set<string>(preset.description?.ExtractImages(false)));
 | 
					            parts.push(new Set<string>(preset.description?.ExtractImages(false)));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        for (const pointRenderingConfig of this.mapRendering) {
 | 
				
			||||||
 | 
					            parts.push(pointRenderingConfig.ExtractImages())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        const allIcons = new Set<string>();
 | 
					        const allIcons = new Set<string>();
 | 
				
			||||||
        for (const part of parts) {
 | 
					        for (const part of parts) {
 | 
				
			||||||
            part?.forEach(allIcons.add, allIcons);
 | 
					            part?.forEach(allIcons.add, allIcons);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return allIcons;
 | 
					        return allIcons;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -25,7 +25,6 @@ export default class LayoutConfig {
 | 
				
			||||||
    public readonly startLat: number;
 | 
					    public readonly startLat: number;
 | 
				
			||||||
    public readonly startLon: number;
 | 
					    public readonly startLon: number;
 | 
				
			||||||
    public readonly widenFactor: number;
 | 
					    public readonly widenFactor: number;
 | 
				
			||||||
    public readonly roamingRenderings: TagRenderingConfig[];
 | 
					 | 
				
			||||||
    public readonly defaultBackgroundId?: string;
 | 
					    public readonly defaultBackgroundId?: string;
 | 
				
			||||||
    public layers: LayerConfig[];
 | 
					    public layers: LayerConfig[];
 | 
				
			||||||
    public tileLayerSources: TilesourceConfig[]
 | 
					    public tileLayerSources: TilesourceConfig[]
 | 
				
			||||||
| 
						 | 
					@ -97,44 +96,11 @@ export default class LayoutConfig {
 | 
				
			||||||
            throw "Widenfactor is very big, use a value between 1 and 5 (current value is "+json.widenFactor+") at "+context
 | 
					            throw "Widenfactor is very big, use a value between 1 and 5 (current value is "+json.widenFactor+") at "+context
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.widenFactor = json.widenFactor ?? 1.5;
 | 
					        this.widenFactor = json.widenFactor ?? 1.5;
 | 
				
			||||||
        this.roamingRenderings = (json.roamingRenderings ?? []).map((tr, i) => {
 | 
					     
 | 
				
			||||||
                if (typeof tr === "string") {
 | 
					 | 
				
			||||||
                    if (SharedTagRenderings.SharedTagRendering.get(tr) !== undefined) {
 | 
					 | 
				
			||||||
                        return SharedTagRenderings.SharedTagRendering.get(tr);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                return new TagRenderingConfig(tr, undefined, `${this.id}.roaming_renderings[${i}]`);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        this.defaultBackgroundId = json.defaultBackgroundId;
 | 
					        this.defaultBackgroundId = json.defaultBackgroundId;
 | 
				
			||||||
        this.tileLayerSources = (json.tileLayerSources??[]).map((config, i) => new TilesourceConfig(config, `${this.id}.tileLayerSources[${i}]`))
 | 
					        this.tileLayerSources = (json.tileLayerSources??[]).map((config, i) => new TilesourceConfig(config, `${this.id}.tileLayerSources[${i}]`))
 | 
				
			||||||
        this.layers = LayoutConfig.ExtractLayers(json, official, context);
 | 
					        this.layers = LayoutConfig.ExtractLayers(json, official, context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // ALl the layers are constructed, let them share tagRenderings now!
 | 
					 | 
				
			||||||
        const roaming: { r, source: LayerConfig }[] = []
 | 
					 | 
				
			||||||
        for (const layer of this.layers) {
 | 
					 | 
				
			||||||
            roaming.push({r: layer.GetRoamingRenderings(), source: layer});
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (const layer of this.layers) {
 | 
					 | 
				
			||||||
            for (const r of roaming) {
 | 
					 | 
				
			||||||
                if (r.source == layer) {
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                layer.AddRoamingRenderings(r.r);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (const layer of this.layers) {
 | 
					 | 
				
			||||||
            layer.AddRoamingRenderings(
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    titleIcons: [],
 | 
					 | 
				
			||||||
                    iconOverlays: [],
 | 
					 | 
				
			||||||
                    tagRenderings: this.roamingRenderings
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.clustering = {
 | 
					        this.clustering = {
 | 
				
			||||||
            maxZoom: 16,
 | 
					            maxZoom: 16,
 | 
				
			||||||
            minNeededElements: 25,
 | 
					            minNeededElements: 25,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										244
									
								
								Models/ThemeConfig/PointRenderingConfig.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								Models/ThemeConfig/PointRenderingConfig.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,244 @@
 | 
				
			||||||
 | 
					import PointRenderingConfigJson from "./Json/PointRenderingConfigJson";
 | 
				
			||||||
 | 
					import TagRenderingConfig from "./TagRenderingConfig";
 | 
				
			||||||
 | 
					import {TagsFilter} from "../../Logic/Tags/TagsFilter";
 | 
				
			||||||
 | 
					import SharedTagRenderings from "../../Customizations/SharedTagRenderings";
 | 
				
			||||||
 | 
					import {TagUtils} from "../../Logic/Tags/TagUtils";
 | 
				
			||||||
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
 | 
					import Svg from "../../Svg";
 | 
				
			||||||
 | 
					import WithContextLoader from "./WithContextLoader";
 | 
				
			||||||
 | 
					import {UIEventSource} from "../../Logic/UIEventSource";
 | 
				
			||||||
 | 
					import BaseUIElement from "../../UI/BaseUIElement";
 | 
				
			||||||
 | 
					import {FixedUiElement} from "../../UI/Base/FixedUiElement";
 | 
				
			||||||
 | 
					import Img from "../../UI/Base/Img";
 | 
				
			||||||
 | 
					import Combine from "../../UI/Base/Combine";
 | 
				
			||||||
 | 
					import {VariableUiElement} from "../../UI/Base/VariableUIElement";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class PointRenderingConfig extends WithContextLoader {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public readonly icon: TagRenderingConfig;
 | 
				
			||||||
 | 
					    public readonly iconOverlays: { if: TagsFilter; then: TagRenderingConfig; badge: boolean }[];
 | 
				
			||||||
 | 
					    public readonly iconSize: TagRenderingConfig;
 | 
				
			||||||
 | 
					    public readonly label: TagRenderingConfig;
 | 
				
			||||||
 | 
					    public readonly rotation: TagRenderingConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(json: PointRenderingConfigJson, context: string) {
 | 
				
			||||||
 | 
					        super(json, context)
 | 
				
			||||||
 | 
					        this.icon = this.tr("icon", "");
 | 
				
			||||||
 | 
					        this.iconOverlays = (json.iconOverlays ?? []).map((overlay, i) => {
 | 
				
			||||||
 | 
					            let tr = new TagRenderingConfig(
 | 
				
			||||||
 | 
					                overlay.then,
 | 
				
			||||||
 | 
					                undefined,
 | 
				
			||||||
 | 
					                `iconoverlays.${i}`
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (
 | 
				
			||||||
 | 
					                typeof overlay.then === "string" &&
 | 
				
			||||||
 | 
					                SharedTagRenderings.SharedIcons.get(overlay.then) !== undefined
 | 
				
			||||||
 | 
					            ) {
 | 
				
			||||||
 | 
					                tr = SharedTagRenderings.SharedIcons.get(overlay.then);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                if: TagUtils.Tag(overlay.if),
 | 
				
			||||||
 | 
					                then: tr,
 | 
				
			||||||
 | 
					                badge: overlay.badge ?? false,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const iconPath = this.icon.GetRenderValue({id: "node/-1"}).txt;
 | 
				
			||||||
 | 
					        if (iconPath.startsWith(Utils.assets_path)) {
 | 
				
			||||||
 | 
					            const iconKey = iconPath.substr(Utils.assets_path.length);
 | 
				
			||||||
 | 
					            if (Svg.All[iconKey] === undefined) {
 | 
				
			||||||
 | 
					                throw "Builtin SVG asset not found: " + iconPath;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.iconSize = this.tr("iconSize", "40,40,center");
 | 
				
			||||||
 | 
					        this.label = this.tr("label", "");
 | 
				
			||||||
 | 
					        this.rotation = this.tr("rotation", "0");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ExtractImages(): Set<string> {
 | 
				
			||||||
 | 
					        const parts: Set<string>[] = [];
 | 
				
			||||||
 | 
					        parts.push(this.icon?.ExtractImages(true));
 | 
				
			||||||
 | 
					        parts.push(
 | 
				
			||||||
 | 
					            ...this.iconOverlays?.map((overlay) => overlay.then.ExtractImages(true))
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const allIcons = new Set<string>();
 | 
				
			||||||
 | 
					        for (const part of parts) {
 | 
				
			||||||
 | 
					            part?.forEach(allIcons.add, allIcons);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return allIcons;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public GenerateLeafletStyle(
 | 
				
			||||||
 | 
					        tags: UIEventSource<any>,
 | 
				
			||||||
 | 
					        clickable: boolean
 | 
				
			||||||
 | 
					    ): 
 | 
				
			||||||
 | 
					       {
 | 
				
			||||||
 | 
					            html: BaseUIElement;
 | 
				
			||||||
 | 
					            iconSize: [number, number];
 | 
				
			||||||
 | 
					            iconAnchor: [number, number];
 | 
				
			||||||
 | 
					            popupAnchor: [number, number];
 | 
				
			||||||
 | 
					            iconUrl: string;
 | 
				
			||||||
 | 
					            className: string;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					     {
 | 
				
			||||||
 | 
					        function num(str, deflt = 40) {
 | 
				
			||||||
 | 
					            const n = Number(str);
 | 
				
			||||||
 | 
					            if (isNaN(n)) {
 | 
				
			||||||
 | 
					                return deflt;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return n;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function rendernum(tr: TagRenderingConfig, deflt: number) {
 | 
				
			||||||
 | 
					            const str = Number(render(tr, "" + deflt));
 | 
				
			||||||
 | 
					            const n = Number(str);
 | 
				
			||||||
 | 
					            if (isNaN(n)) {
 | 
				
			||||||
 | 
					                return deflt;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return n;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function render(tr: TagRenderingConfig, deflt?: string) {
 | 
				
			||||||
 | 
					            if (tags === undefined) {
 | 
				
			||||||
 | 
					                return deflt
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            const str = tr?.GetRenderValue(tags.data)?.txt ?? deflt;
 | 
				
			||||||
 | 
					            return Utils.SubstituteKeys(str, tags.data).replace(/{.*}/g, "");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const iconSize = render(this.iconSize, "40,40,center").split(",");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const iconW = num(iconSize[0]);
 | 
				
			||||||
 | 
					        let iconH = num(iconSize[1]);
 | 
				
			||||||
 | 
					        const mode = iconSize[2]?.trim()?.toLowerCase() ?? "center";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let anchorW = iconW / 2;
 | 
				
			||||||
 | 
					        let anchorH = iconH / 2;
 | 
				
			||||||
 | 
					        if (mode === "left") {
 | 
				
			||||||
 | 
					            anchorW = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (mode === "right") {
 | 
				
			||||||
 | 
					            anchorW = iconW;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (mode === "top") {
 | 
				
			||||||
 | 
					            anchorH = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (mode === "bottom") {
 | 
				
			||||||
 | 
					            anchorH = iconH;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const iconUrlStatic = render(this.icon);
 | 
				
			||||||
 | 
					        const self = this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function genHtmlFromString(sourcePart: string, rotation: string): BaseUIElement {
 | 
				
			||||||
 | 
					            const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`;
 | 
				
			||||||
 | 
					            let html: BaseUIElement = new FixedUiElement(
 | 
				
			||||||
 | 
					                `<img src="${sourcePart}" style="${style}" />`
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            const match = sourcePart.match(/([a-zA-Z0-9_]*):([^;]*)/);
 | 
				
			||||||
 | 
					            if (match !== null && Svg.All[match[1] + ".svg"] !== undefined) {
 | 
				
			||||||
 | 
					                html = new Img(
 | 
				
			||||||
 | 
					                    (Svg.All[match[1] + ".svg"] as string).replace(
 | 
				
			||||||
 | 
					                        /#000000/g,
 | 
				
			||||||
 | 
					                        match[2]
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    true
 | 
				
			||||||
 | 
					                ).SetStyle(style);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return html;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const mappedHtml = tags?.map((tgs) => {
 | 
				
			||||||
 | 
					            // What do you mean, 'tgs' is never read?
 | 
				
			||||||
 | 
					            // It is read implicitly in the 'render' method
 | 
				
			||||||
 | 
					            const iconUrl = render(self.icon);
 | 
				
			||||||
 | 
					            const rotation = render(self.rotation, "0deg");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let htmlParts: BaseUIElement[] = [];
 | 
				
			||||||
 | 
					            let sourceParts = Utils.NoNull(
 | 
				
			||||||
 | 
					                iconUrl.split(";").filter((prt) => prt != "")
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            for (const sourcePart of sourceParts) {
 | 
				
			||||||
 | 
					                htmlParts.push(genHtmlFromString(sourcePart, rotation));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let badges = [];
 | 
				
			||||||
 | 
					            for (const iconOverlay of self.iconOverlays) {
 | 
				
			||||||
 | 
					                if (!iconOverlay.if.matchesProperties(tgs)) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (iconOverlay.badge) {
 | 
				
			||||||
 | 
					                    const badgeParts: BaseUIElement[] = [];
 | 
				
			||||||
 | 
					                    const renderValue = iconOverlay
 | 
				
			||||||
 | 
					                        .then
 | 
				
			||||||
 | 
					                        .GetRenderValue(tgs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (renderValue === undefined) {
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    const partDefs = renderValue.txt.split(";")
 | 
				
			||||||
 | 
					                        .filter((prt) => prt != "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    for (const badgePartStr of partDefs) {
 | 
				
			||||||
 | 
					                        badgeParts.push(genHtmlFromString(badgePartStr, "0"));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    const badgeCompound = new Combine(badgeParts).SetStyle(
 | 
				
			||||||
 | 
					                        "display:flex;position:relative;width:100%;height:100%;"
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    badges.push(badgeCompound);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    htmlParts.push(
 | 
				
			||||||
 | 
					                        genHtmlFromString(iconOverlay.then.GetRenderValue(tgs).txt, "0")
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (badges.length > 0) {
 | 
				
			||||||
 | 
					                const badgesComponent = new Combine(badges).SetStyle(
 | 
				
			||||||
 | 
					                    "display:flex;height:50%;width:100%;position:absolute;top:50%;left:50%;"
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                htmlParts.push(badgesComponent);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (sourceParts.length == 0) {
 | 
				
			||||||
 | 
					                iconH = 0;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                const label = self.label
 | 
				
			||||||
 | 
					                    ?.GetRenderValue(tgs)
 | 
				
			||||||
 | 
					                    ?.Subs(tgs)
 | 
				
			||||||
 | 
					                    ?.SetClass("block text-center")
 | 
				
			||||||
 | 
					                    ?.SetStyle("margin-top: " + (iconH + 2) + "px");
 | 
				
			||||||
 | 
					                if (label !== undefined) {
 | 
				
			||||||
 | 
					                    htmlParts.push(
 | 
				
			||||||
 | 
					                        new Combine([label]).SetClass("flex flex-col items-center")
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (e) {
 | 
				
			||||||
 | 
					                console.error(e, tgs);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return new Combine(htmlParts);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					                html: mappedHtml === undefined ? new FixedUiElement(self.icon.render.txt) : new VariableUiElement(mappedHtml),
 | 
				
			||||||
 | 
					                iconSize: [iconW, iconH],
 | 
				
			||||||
 | 
					                iconAnchor: [anchorW, anchorH],
 | 
				
			||||||
 | 
					                popupAnchor: [0, 3 - anchorH],
 | 
				
			||||||
 | 
					                iconUrl: iconUrlStatic,
 | 
				
			||||||
 | 
					                className: clickable
 | 
				
			||||||
 | 
					                    ? "leaflet-div-icon"
 | 
				
			||||||
 | 
					                    : "leaflet-div-icon unclickable",
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -221,7 +221,6 @@ export default class TagRenderingConfig {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Returns true if it is known or not shown, false if the question should be asked
 | 
					     * Returns true if it is known or not shown, false if the question should be asked
 | 
				
			||||||
     * @constructor
 | 
					     * @constructor
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										117
									
								
								Models/ThemeConfig/WithContextLoader.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								Models/ThemeConfig/WithContextLoader.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,117 @@
 | 
				
			||||||
 | 
					import TagRenderingConfig from "./TagRenderingConfig";
 | 
				
			||||||
 | 
					import SharedTagRenderings from "../../Customizations/SharedTagRenderings";
 | 
				
			||||||
 | 
					import {TagRenderingConfigJson} from "./Json/TagRenderingConfigJson";
 | 
				
			||||||
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class WithContextLoader {
 | 
				
			||||||
 | 
					    private readonly _json: any;
 | 
				
			||||||
 | 
					    private readonly _context: string;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    constructor(json: any, context: string) {
 | 
				
			||||||
 | 
					        this._json = json;
 | 
				
			||||||
 | 
					        this._context = context;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Given a key, gets the corresponding property from the json (or the default if not found
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * The found value is interpreted as a tagrendering and fetched/parsed
 | 
				
			||||||
 | 
					     * */
 | 
				
			||||||
 | 
					    public tr(key: string, deflt) {
 | 
				
			||||||
 | 
					        const v = this._json[key];
 | 
				
			||||||
 | 
					        if (v === undefined || v === null) {
 | 
				
			||||||
 | 
					            if (deflt === undefined) {
 | 
				
			||||||
 | 
					                return undefined;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return new TagRenderingConfig(
 | 
				
			||||||
 | 
					                deflt,
 | 
				
			||||||
 | 
					                undefined,
 | 
				
			||||||
 | 
					                `${this._context}.${key}.default value`
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (typeof v === "string") {
 | 
				
			||||||
 | 
					            const shared = SharedTagRenderings.SharedTagRendering.get(v);
 | 
				
			||||||
 | 
					            if (shared) {
 | 
				
			||||||
 | 
					                return shared;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return new TagRenderingConfig(
 | 
				
			||||||
 | 
					            v,
 | 
				
			||||||
 | 
					            undefined,
 | 
				
			||||||
 | 
					            `${this._context}.${key}`
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Converts a list of tagRenderingCOnfigJSON in to TagRenderingConfig
 | 
				
			||||||
 | 
					     * A string is interpreted as a name to call
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public trs(
 | 
				
			||||||
 | 
					        tagRenderings?: (string | { builtin: string, override: any } | TagRenderingConfigJson)[],
 | 
				
			||||||
 | 
					        readOnly = false
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        if (tagRenderings === undefined) {
 | 
				
			||||||
 | 
					            return [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        const context = this._context
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        const renderings: TagRenderingConfig[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (let i = 0; i < tagRenderings.length; i++) {
 | 
				
			||||||
 | 
					           let renderingJson=  tagRenderings[i]
 | 
				
			||||||
 | 
					            if (typeof renderingJson === "string") {
 | 
				
			||||||
 | 
					                renderingJson = {builtin: renderingJson, override: undefined}
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (renderingJson["builtin"] !== undefined) {
 | 
				
			||||||
 | 
					                const renderingId = renderingJson["builtin"]
 | 
				
			||||||
 | 
					                if (renderingId === "questions") {
 | 
				
			||||||
 | 
					                    if (readOnly) {
 | 
				
			||||||
 | 
					                        throw `A tagrendering has a question, but asking a question does not make sense here: is it a title icon or a geojson-layer? ${context}. The offending tagrendering is ${JSON.stringify(
 | 
				
			||||||
 | 
					                            renderingJson
 | 
				
			||||||
 | 
					                        )}`;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    const tr = new TagRenderingConfig("questions", undefined, context);
 | 
				
			||||||
 | 
					                renderings.push(tr)
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (renderingJson["override"] !== undefined) {
 | 
				
			||||||
 | 
					                    const sharedJson = SharedTagRenderings.SharedTagRenderingJson.get(renderingId)
 | 
				
			||||||
 | 
					                    const tr = new TagRenderingConfig(
 | 
				
			||||||
 | 
					                        Utils.Merge(renderingJson["override"], sharedJson),
 | 
				
			||||||
 | 
					                        undefined,
 | 
				
			||||||
 | 
					                        `${context}.tagrendering[${i}]+override`
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    renderings.push(tr)
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const shared = SharedTagRenderings.SharedTagRendering.get(renderingId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (shared !== undefined) {
 | 
				
			||||||
 | 
					                    renderings.push( shared)
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (Utils.runningFromConsole) {
 | 
				
			||||||
 | 
					                   continue
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const keys = Array.from(                    SharedTagRenderings.SharedTagRendering.keys()                );
 | 
				
			||||||
 | 
					                throw `Predefined tagRendering ${renderingId} not found in ${context}.\n    Try one of ${keys.join(
 | 
				
			||||||
 | 
					                    ", "
 | 
				
			||||||
 | 
					                )}\n    If you intent to output this text literally, use {\"render\": <your text>} instead"}`;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const tr = new TagRenderingConfig(
 | 
				
			||||||
 | 
					                <TagRenderingConfigJson>renderingJson,
 | 
				
			||||||
 | 
					                undefined,
 | 
				
			||||||
 | 
					                `${context}.tagrendering[${i}]`
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            renderings.push(tr)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return renderings;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * This script reads all theme and layer files and reformats them inplace
 | 
					 * This script reads all theme and layer files and reformats them inplace
 | 
				
			||||||
 * Use with caution, make a commit beforehand!
 | 
					 * Use with caution, make a commit beforehand!
 | 
				
			||||||
| 
						 | 
					@ -6,29 +5,47 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ScriptUtils from "./ScriptUtils";
 | 
					import ScriptUtils from "./ScriptUtils";
 | 
				
			||||||
import {readFileSync, writeFileSync} from "fs";
 | 
					import {writeFileSync} from "fs";
 | 
				
			||||||
import {tag} from "@turf/turf";
 | 
					 | 
				
			||||||
import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson";
 | 
					import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * In place fix
 | 
					 * In place fix
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
function fixLayerConfig(config: LayerConfigJson) : void{
 | 
					function fixLayerConfig(config: LayerConfigJson): void {
 | 
				
			||||||
    if(config.tagRenderings === undefined){
 | 
					    if (config.tagRenderings !== undefined) {
 | 
				
			||||||
        return
 | 
					        for (const tagRendering of config.tagRenderings) {
 | 
				
			||||||
    }
 | 
					            if (tagRendering["#"] !== undefined) {
 | 
				
			||||||
    
 | 
					                tagRendering["id"] = tagRendering["#"]
 | 
				
			||||||
    for (const tagRendering of config.tagRenderings) {
 | 
					                delete tagRendering["#"]
 | 
				
			||||||
        if(tagRendering["#"] !== undefined){
 | 
					            }
 | 
				
			||||||
            tagRendering["id"] = tagRendering["#"]
 | 
					            if (tagRendering["id"] === undefined) {
 | 
				
			||||||
            delete tagRendering["#"]
 | 
					                if (tagRendering["freeform"]?.key !== undefined) {
 | 
				
			||||||
        }
 | 
					                    tagRendering["id"] = config.id + "-" + tagRendering["freeform"]["key"]
 | 
				
			||||||
        if(tagRendering["id"] === undefined){
 | 
					                }
 | 
				
			||||||
            if(tagRendering["freeform"]?.key !== undefined ) {
 | 
					 | 
				
			||||||
                tagRendering["id"] = config.id+"-"+tagRendering["freeform"]["key"]
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if(config.mapRendering === undefined){
 | 
				
			||||||
 | 
					        // This is a legacy format, lets create a pointRendering
 | 
				
			||||||
 | 
					        let location: ("point"|"centroid")[] = ["point"]
 | 
				
			||||||
 | 
					        if(config.wayHandling === 2){
 | 
				
			||||||
 | 
					            location = ["point", "centroid"]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        config.mapRendering = [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                icon: config["icon"],
 | 
				
			||||||
 | 
					                iconOverlays: config["iconOverlays"],
 | 
				
			||||||
 | 
					                label: config["label"],
 | 
				
			||||||
 | 
					                iconSize: config["iconSize"],
 | 
				
			||||||
 | 
					                location,
 | 
				
			||||||
 | 
					                rotation: config["rotation"]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const layerFiles = ScriptUtils.getLayerFiles();
 | 
					const layerFiles = ScriptUtils.getLayerFiles();
 | 
				
			||||||
| 
						 | 
					@ -40,7 +57,7 @@ for (const layerFile of layerFiles) {
 | 
				
			||||||
const themeFiles = ScriptUtils.getThemeFiles()
 | 
					const themeFiles = ScriptUtils.getThemeFiles()
 | 
				
			||||||
for (const themeFile of themeFiles) {
 | 
					for (const themeFile of themeFiles) {
 | 
				
			||||||
    for (const layerConfig of themeFile.parsed.layers ?? []) {
 | 
					    for (const layerConfig of themeFile.parsed.layers ?? []) {
 | 
				
			||||||
        if(typeof layerConfig === "string" || layerConfig["builtin"]!== undefined){
 | 
					        if (typeof layerConfig === "string" || layerConfig["builtin"] !== undefined) {
 | 
				
			||||||
            continue
 | 
					            continue
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // @ts-ignore
 | 
					        // @ts-ignore
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue