forked from MapComplete/MapComplete
		
	Merge develop
This commit is contained in:
		
						commit
						cc22c5b0fb
					
				
					 17 changed files with 537 additions and 134 deletions
				
			
		| 
						 | 
					@ -27,7 +27,8 @@ export default class TagRenderingConfig {
 | 
				
			||||||
        readonly type: string,
 | 
					        readonly type: string,
 | 
				
			||||||
        readonly addExtraTags: TagsFilter[];
 | 
					        readonly addExtraTags: TagsFilter[];
 | 
				
			||||||
        readonly inline: boolean,
 | 
					        readonly inline: boolean,
 | 
				
			||||||
        readonly default?: string
 | 
					        readonly default?: string,
 | 
				
			||||||
 | 
					        readonly helperArgs?: (string | number | boolean)[]
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    readonly multiAnswer: boolean;
 | 
					    readonly multiAnswer: boolean;
 | 
				
			||||||
| 
						 | 
					@ -76,8 +77,8 @@ export default class TagRenderingConfig {
 | 
				
			||||||
                addExtraTags: json.freeform.addExtraTags?.map((tg, i) =>
 | 
					                addExtraTags: json.freeform.addExtraTags?.map((tg, i) =>
 | 
				
			||||||
                    FromJSON.Tag(tg, `${context}.extratag[${i}]`)) ?? [],
 | 
					                    FromJSON.Tag(tg, `${context}.extratag[${i}]`)) ?? [],
 | 
				
			||||||
                inline: json.freeform.inline ?? false,
 | 
					                inline: json.freeform.inline ?? false,
 | 
				
			||||||
                default: json.freeform.default
 | 
					                default: json.freeform.default,
 | 
				
			||||||
 | 
					                helperArgs: json.freeform.helperArgs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (json.freeform["extraTags"] !== undefined) {
 | 
					            if (json.freeform["extraTags"] !== undefined) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,7 @@ export interface TagRenderingConfigJson {
 | 
				
			||||||
     * Allow freeform text input from the user
 | 
					     * Allow freeform text input from the user
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    freeform?: {
 | 
					    freeform?: {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * If this key is present, then 'render' is used to display the value.
 | 
					         * If this key is present, then 'render' is used to display the value.
 | 
				
			||||||
         * If this is undefined, the rendering is _always_ shown
 | 
					         * If this is undefined, the rendering is _always_ shown
 | 
				
			||||||
| 
						 | 
					@ -40,6 +41,11 @@ export interface TagRenderingConfigJson {
 | 
				
			||||||
         * See Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values
 | 
					         * See Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values
 | 
				
			||||||
         */
 | 
					         */
 | 
				
			||||||
        type?: string,
 | 
					        type?: string,
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Extra parameters to initialize the input helper arguments.
 | 
				
			||||||
 | 
					         * For semantics, see the 'SpecialInputElements.md'
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        helperArgs?: (string | number | boolean)[];
 | 
				
			||||||
        /**
 | 
					        /**
 | 
				
			||||||
         * If a value is added with the textfield, these extra tag is addded.
 | 
					         * If a value is added with the textfield, these extra tag is addded.
 | 
				
			||||||
         * Useful to add a 'fixme=freeform textfield used - to be checked'
 | 
					         * Useful to add a 'fixme=freeform textfield used - to be checked'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										7
									
								
								Svg.ts
									
										
									
									
									
								
							
							
						
						
									
										7
									
								
								Svg.ts
									
										
									
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
						 | 
					@ -5,6 +5,7 @@ import Loc from "../../Models/Loc";
 | 
				
			||||||
import BaseLayer from "../../Models/BaseLayer";
 | 
					import BaseLayer from "../../Models/BaseLayer";
 | 
				
			||||||
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers";
 | 
					import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers";
 | 
				
			||||||
import {Map} from "leaflet";
 | 
					import {Map} from "leaflet";
 | 
				
			||||||
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Minimap extends BaseUIElement {
 | 
					export default class Minimap extends BaseUIElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,11 +16,13 @@ export default class Minimap extends BaseUIElement {
 | 
				
			||||||
    private readonly _location: UIEventSource<Loc>;
 | 
					    private readonly _location: UIEventSource<Loc>;
 | 
				
			||||||
    private _isInited = false;
 | 
					    private _isInited = false;
 | 
				
			||||||
    private _allowMoving: boolean;
 | 
					    private _allowMoving: boolean;
 | 
				
			||||||
 | 
					    private readonly _leafletoptions: any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(options?: {
 | 
					    constructor(options?: {
 | 
				
			||||||
                    background?: UIEventSource<BaseLayer>,
 | 
					                    background?: UIEventSource<BaseLayer>,
 | 
				
			||||||
                    location?: UIEventSource<Loc>,
 | 
					                    location?: UIEventSource<Loc>,
 | 
				
			||||||
                    allowMoving?: boolean
 | 
					                    allowMoving?: boolean,
 | 
				
			||||||
 | 
					                    leafletOptions?: any
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        super()
 | 
					        super()
 | 
				
			||||||
| 
						 | 
					@ -28,6 +31,7 @@ export default class Minimap extends BaseUIElement {
 | 
				
			||||||
        this._location = options?.location ?? new UIEventSource<Loc>({lat: 0, lon: 0, zoom: 1})
 | 
					        this._location = options?.location ?? new UIEventSource<Loc>({lat: 0, lon: 0, zoom: 1})
 | 
				
			||||||
        this._id = "minimap" + Minimap._nextId;
 | 
					        this._id = "minimap" + Minimap._nextId;
 | 
				
			||||||
        this._allowMoving = options.allowMoving ?? true;
 | 
					        this._allowMoving = options.allowMoving ?? true;
 | 
				
			||||||
 | 
					        this._leafletoptions = options.leafletOptions ?? {}
 | 
				
			||||||
        Minimap._nextId++
 | 
					        Minimap._nextId++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -72,8 +76,8 @@ export default class Minimap extends BaseUIElement {
 | 
				
			||||||
        const location = this._location;
 | 
					        const location = this._location;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let currentLayer = this._background.data.layer()
 | 
					        let currentLayer = this._background.data.layer()
 | 
				
			||||||
        const map = L.map(this._id, {
 | 
					        const options = {
 | 
				
			||||||
            center: [location.data?.lat ?? 0, location.data?.lon ?? 0],
 | 
					            center: <[number, number]> [location.data?.lat ?? 0, location.data?.lon ?? 0],
 | 
				
			||||||
            zoom: location.data?.zoom ?? 2,
 | 
					            zoom: location.data?.zoom ?? 2,
 | 
				
			||||||
            layers: [currentLayer],
 | 
					            layers: [currentLayer],
 | 
				
			||||||
            zoomControl: false,
 | 
					            zoomControl: false,
 | 
				
			||||||
| 
						 | 
					@ -85,7 +89,11 @@ export default class Minimap extends BaseUIElement {
 | 
				
			||||||
            touchZoom: this._allowMoving,
 | 
					            touchZoom: this._allowMoving,
 | 
				
			||||||
            // Disabling this breaks the geojson layer - don't ask me why!  zoomAnimation: this._allowMoving,
 | 
					            // Disabling this breaks the geojson layer - don't ask me why!  zoomAnimation: this._allowMoving,
 | 
				
			||||||
            fadeAnimation: this._allowMoving
 | 
					            fadeAnimation: this._allowMoving
 | 
				
			||||||
        });
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Utils.Merge(this._leafletoptions, options)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        const map = L.map(this._id, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        map.setMaxBounds(
 | 
					        map.setMaxBounds(
 | 
				
			||||||
            [[-100, -200], [100, 200]]
 | 
					            [[-100, -200], [100, 200]]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										185
									
								
								UI/Input/LengthInput.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								UI/Input/LengthInput.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,185 @@
 | 
				
			||||||
 | 
					import {InputElement} from "./InputElement";
 | 
				
			||||||
 | 
					import {UIEventSource} from "../../Logic/UIEventSource";
 | 
				
			||||||
 | 
					import Combine from "../Base/Combine";
 | 
				
			||||||
 | 
					import Svg from "../../Svg";
 | 
				
			||||||
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
 | 
					import Loc from "../../Models/Loc";
 | 
				
			||||||
 | 
					import {GeoOperations} from "../../Logic/GeoOperations";
 | 
				
			||||||
 | 
					import DirectionInput from "./DirectionInput";
 | 
				
			||||||
 | 
					import {RadioButton} from "./RadioButton";
 | 
				
			||||||
 | 
					import {FixedInputElement} from "./FixedInputElement";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Selects a length after clicking on the minimap, in meters
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export default class LengthInput extends InputElement<string> {
 | 
				
			||||||
 | 
					    private readonly _location: UIEventSource<Loc>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
 | 
				
			||||||
 | 
					    private readonly value: UIEventSource<string>;
 | 
				
			||||||
 | 
					    private background;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(mapBackground: UIEventSource<any>,
 | 
				
			||||||
 | 
					                location: UIEventSource<Loc>,
 | 
				
			||||||
 | 
					                value?: UIEventSource<string>) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        this._location = location;
 | 
				
			||||||
 | 
					        this.value = value ?? new UIEventSource<string>(undefined);
 | 
				
			||||||
 | 
					        this.background = mapBackground;
 | 
				
			||||||
 | 
					        this.SetClass("block")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    GetValue(): UIEventSource<string> {
 | 
				
			||||||
 | 
					        return this.value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IsValid(str: string): boolean {
 | 
				
			||||||
 | 
					        const t = Number(str)
 | 
				
			||||||
 | 
					        return !isNaN(t) && t >= 0 && t <= 360;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected InnerConstructElement(): HTMLElement {
 | 
				
			||||||
 | 
					        const modeElement = new RadioButton([
 | 
				
			||||||
 | 
					            new FixedInputElement("Measure", "measure"),
 | 
				
			||||||
 | 
					            new FixedInputElement("Move", "move")
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					        // @ts-ignore
 | 
				
			||||||
 | 
					        let map = undefined
 | 
				
			||||||
 | 
					        if (!Utils.runningFromConsole) {
 | 
				
			||||||
 | 
					            map = DirectionInput.constructMinimap({
 | 
				
			||||||
 | 
					                background: this.background,
 | 
				
			||||||
 | 
					                allowMoving: false,
 | 
				
			||||||
 | 
					                location: this._location,
 | 
				
			||||||
 | 
					                leafletOptions: {
 | 
				
			||||||
 | 
					                    tap: true
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const element = new Combine([
 | 
				
			||||||
 | 
					            new Combine([Svg.length_crosshair_svg().SetStyle(
 | 
				
			||||||
 | 
					                `position: absolute;top: 0;left: 0;transform:rotate(${this.value.data ?? 0}deg);`)
 | 
				
			||||||
 | 
					            ])
 | 
				
			||||||
 | 
					                .SetClass("block length-crosshair-svg relative")
 | 
				
			||||||
 | 
					                .SetStyle("z-index: 1000; visibility: hidden"),
 | 
				
			||||||
 | 
					            map?.SetClass("w-full h-full block absolute top-0 left-O overflow-hidden"),
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					            .SetClass("relative block bg-white border border-black rounded-3xl overflow-hidden")
 | 
				
			||||||
 | 
					            .ConstructElement()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.RegisterTriggers(element, map?.leafletMap)
 | 
				
			||||||
 | 
					        element.style.overflow = "hidden"
 | 
				
			||||||
 | 
					        element.style.display = "block"
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					      return element
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private RegisterTriggers(htmlElement: HTMLElement, leafletMap: UIEventSource<L.Map>) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let firstClickXY: [number, number] = undefined
 | 
				
			||||||
 | 
					        let lastClickXY: [number, number] = undefined
 | 
				
			||||||
 | 
					        const self = this;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function onPosChange(x: number, y: number, isDown: boolean, isUp?: boolean) {
 | 
				
			||||||
 | 
					            if (x === undefined || y === undefined) {
 | 
				
			||||||
 | 
					                // Touch end
 | 
				
			||||||
 | 
					                firstClickXY = undefined;
 | 
				
			||||||
 | 
					                lastClickXY = undefined;
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const rect = htmlElement.getBoundingClientRect();
 | 
				
			||||||
 | 
					            // From the central part of location
 | 
				
			||||||
 | 
					            const dx = x - rect.left;
 | 
				
			||||||
 | 
					            const dy = y - rect.top;
 | 
				
			||||||
 | 
					            if (isDown) {
 | 
				
			||||||
 | 
					                if (lastClickXY === undefined && firstClickXY === undefined) {
 | 
				
			||||||
 | 
					                    firstClickXY = [dx, dy];
 | 
				
			||||||
 | 
					                } else if (firstClickXY !== undefined && lastClickXY === undefined) {
 | 
				
			||||||
 | 
					                    lastClickXY = [dx, dy]
 | 
				
			||||||
 | 
					                } else if (firstClickXY !== undefined && lastClickXY !== undefined) {
 | 
				
			||||||
 | 
					                    // we measure again
 | 
				
			||||||
 | 
					                    firstClickXY = [dx, dy]
 | 
				
			||||||
 | 
					                    lastClickXY = undefined;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (isUp) {
 | 
				
			||||||
 | 
					                const distance = Math.sqrt((dy - firstClickXY[1]) * (dy - firstClickXY[1]) + (dx - firstClickXY[0]) * (dx - firstClickXY[0]))
 | 
				
			||||||
 | 
					                if (distance > 15) {
 | 
				
			||||||
 | 
					                    lastClickXY = [dx, dy]
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            } else if (lastClickXY !== undefined) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const measurementCrosshair = htmlElement.getElementsByClassName("length-crosshair-svg")[0] as HTMLElement
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            const measurementCrosshairInner: HTMLElement = <HTMLElement>measurementCrosshair.firstChild
 | 
				
			||||||
 | 
					            if (firstClickXY === undefined) {
 | 
				
			||||||
 | 
					                measurementCrosshair.style.visibility = "hidden"
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                measurementCrosshair.style.visibility = "unset"
 | 
				
			||||||
 | 
					                measurementCrosshair.style.left = firstClickXY[0] + "px";
 | 
				
			||||||
 | 
					                measurementCrosshair.style.top = firstClickXY[1] + "px"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const angle = 180 * Math.atan2(firstClickXY[1] - dy, firstClickXY[0] - dx) / Math.PI;
 | 
				
			||||||
 | 
					                const angleGeo = (angle + 270) % 360
 | 
				
			||||||
 | 
					                measurementCrosshairInner.style.transform = `rotate(${angleGeo}deg)`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const distance = Math.sqrt((dy - firstClickXY[1]) * (dy - firstClickXY[1]) + (dx - firstClickXY[0]) * (dx - firstClickXY[0]))
 | 
				
			||||||
 | 
					                measurementCrosshairInner.style.width = (distance * 2) + "px"
 | 
				
			||||||
 | 
					                measurementCrosshairInner.style.marginLeft = -distance + "px"
 | 
				
			||||||
 | 
					                measurementCrosshairInner.style.marginTop = -distance + "px"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const leaflet = leafletMap?.data
 | 
				
			||||||
 | 
					                if (leaflet) {
 | 
				
			||||||
 | 
					                    const first = leaflet.layerPointToLatLng(firstClickXY)
 | 
				
			||||||
 | 
					                    const last = leaflet.layerPointToLatLng([dx, dy])
 | 
				
			||||||
 | 
					                    const geoDist = Math.floor(GeoOperations.distanceBetween([first.lng, first.lat], [last.lng, last.lat]) * 100000) / 100
 | 
				
			||||||
 | 
					                    self.value.setData("" + geoDist)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        htmlElement.ontouchstart = (ev: TouchEvent) => {
 | 
				
			||||||
 | 
					            onPosChange(ev.touches[0].clientX, ev.touches[0].clientY, true);
 | 
				
			||||||
 | 
					            ev.preventDefault();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        htmlElement.ontouchmove = (ev: TouchEvent) => {
 | 
				
			||||||
 | 
					            onPosChange(ev.touches[0].clientX, ev.touches[0].clientY, false);
 | 
				
			||||||
 | 
					            ev.preventDefault();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        htmlElement.ontouchend = (ev: TouchEvent) => {
 | 
				
			||||||
 | 
					            onPosChange(undefined, undefined, false, true);
 | 
				
			||||||
 | 
					            ev.preventDefault();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        htmlElement.onmousedown = (ev: MouseEvent) => {
 | 
				
			||||||
 | 
					            onPosChange(ev.clientX, ev.clientY, true);
 | 
				
			||||||
 | 
					            ev.preventDefault();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        htmlElement.onmouseup = (ev) => {
 | 
				
			||||||
 | 
					            onPosChange(ev.clientX, ev.clientY, false, true);
 | 
				
			||||||
 | 
					            ev.preventDefault();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        htmlElement.onmousemove = (ev: MouseEvent) => {
 | 
				
			||||||
 | 
					            onPosChange(ev.clientX, ev.clientY, false);
 | 
				
			||||||
 | 
					            ev.preventDefault();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,8 @@ import {Utils} from "../../Utils";
 | 
				
			||||||
import Loc from "../../Models/Loc";
 | 
					import Loc from "../../Models/Loc";
 | 
				
			||||||
import {Unit} from "../../Customizations/JSON/Denomination";
 | 
					import {Unit} from "../../Customizations/JSON/Denomination";
 | 
				
			||||||
import BaseUIElement from "../BaseUIElement";
 | 
					import BaseUIElement from "../BaseUIElement";
 | 
				
			||||||
 | 
					import LengthInput from "./LengthInput";
 | 
				
			||||||
 | 
					import {GeoOperations} from "../../Logic/GeoOperations";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface TextFieldDef {
 | 
					interface TextFieldDef {
 | 
				
			||||||
    name: string,
 | 
					    name: string,
 | 
				
			||||||
| 
						 | 
					@ -21,14 +23,16 @@ interface TextFieldDef {
 | 
				
			||||||
    reformat?: ((s: string, country?: () => string) => string),
 | 
					    reformat?: ((s: string, country?: () => string) => string),
 | 
				
			||||||
    inputHelper?: (value: UIEventSource<string>, options?: {
 | 
					    inputHelper?: (value: UIEventSource<string>, options?: {
 | 
				
			||||||
        location: [number, number],
 | 
					        location: [number, number],
 | 
				
			||||||
        mapBackgroundLayer?: UIEventSource<any>
 | 
					        mapBackgroundLayer?: UIEventSource<any>,
 | 
				
			||||||
 | 
					        args: (string | number | boolean)[]
 | 
				
			||||||
 | 
					        feature?: any
 | 
				
			||||||
    }) => InputElement<string>,
 | 
					    }) => InputElement<string>,
 | 
				
			||||||
 | 
					 | 
				
			||||||
    inputmode?: string
 | 
					    inputmode?: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class ValidatedTextField {
 | 
					export default class ValidatedTextField {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static bestLayerAt: (location: UIEventSource<Loc>, preferences: UIEventSource<string[]>) => any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static tpList: TextFieldDef[] = [
 | 
					    public static tpList: TextFieldDef[] = [
 | 
				
			||||||
        ValidatedTextField.tp(
 | 
					        ValidatedTextField.tp(
 | 
				
			||||||
| 
						 | 
					@ -63,6 +67,83 @@ export default class ValidatedTextField {
 | 
				
			||||||
                return [year, month, day].join('-');
 | 
					                return [year, month, day].join('-');
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            (value) => new SimpleDatePicker(value)),
 | 
					            (value) => new SimpleDatePicker(value)),
 | 
				
			||||||
 | 
					        ValidatedTextField.tp(
 | 
				
			||||||
 | 
					            "direction",
 | 
				
			||||||
 | 
					            "A geographical direction, in degrees. 0° is north, 90° is east, ... Will return a value between 0 (incl) and 360 (excl)",
 | 
				
			||||||
 | 
					            (str) => {
 | 
				
			||||||
 | 
					                str = "" + str;
 | 
				
			||||||
 | 
					                return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) >= 0 && Number(str) <= 360
 | 
				
			||||||
 | 
					            }, str => str,
 | 
				
			||||||
 | 
					            (value, options) => {
 | 
				
			||||||
 | 
					                const args = options.args ?? []
 | 
				
			||||||
 | 
					                let zoom = 19
 | 
				
			||||||
 | 
					                if (args[0]) {
 | 
				
			||||||
 | 
					                    zoom = Number(args[0])
 | 
				
			||||||
 | 
					                    if (isNaN(zoom)) {
 | 
				
			||||||
 | 
					                        throw "Invalid zoom level for argument at 'length'-input"
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                const location = new UIEventSource<Loc>({
 | 
				
			||||||
 | 
					                    lat: options.location[0],
 | 
				
			||||||
 | 
					                    lon: options.location[1],
 | 
				
			||||||
 | 
					                    zoom: zoom
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                if (args[1]) {
 | 
				
			||||||
 | 
					                    // We have a prefered map!
 | 
				
			||||||
 | 
					                    options.mapBackgroundLayer = ValidatedTextField.bestLayerAt(
 | 
				
			||||||
 | 
					                        location, new UIEventSource<string[]>(args[1].split(","))
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                const di = new DirectionInput(options.mapBackgroundLayer, location, value)
 | 
				
			||||||
 | 
					                di.SetStyle("height: 20rem;");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return di;
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "numeric"
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        ValidatedTextField.tp(
 | 
				
			||||||
 | 
					            "length",
 | 
				
			||||||
 | 
					            "A geographical length in meters (rounded at two points). Will give an extra minimap with a measurement tool. Arguments: [ zoomlevel, preferredBackgroundMapType (comma seperated) ], e.g. `[\"21\", \"map,photo\"]",
 | 
				
			||||||
 | 
					            (str) => {
 | 
				
			||||||
 | 
					                const t = Number(str)
 | 
				
			||||||
 | 
					                return !isNaN(t)
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            str => str,
 | 
				
			||||||
 | 
					            (value, options) => {
 | 
				
			||||||
 | 
					                const args = options.args ?? []
 | 
				
			||||||
 | 
					                let zoom = 19
 | 
				
			||||||
 | 
					                if (args[0]) {
 | 
				
			||||||
 | 
					                    zoom = Number(args[0])
 | 
				
			||||||
 | 
					                    if (isNaN(zoom)) {
 | 
				
			||||||
 | 
					                        throw "Invalid zoom level for argument at 'length'-input"
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Bit of a hack: we project the centerpoint to the closes point on the road - if available
 | 
				
			||||||
 | 
					                if(options.feature){
 | 
				
			||||||
 | 
					                    const lonlat: [number, number] = [...options.location]
 | 
				
			||||||
 | 
					                    lonlat.reverse()
 | 
				
			||||||
 | 
					                    options.location = <[number,number]> GeoOperations.nearestPoint(options.feature, lonlat).geometry.coordinates
 | 
				
			||||||
 | 
					                    options.location.reverse()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                options.feature
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                const location = new UIEventSource<Loc>({
 | 
				
			||||||
 | 
					                    lat: options.location[0],
 | 
				
			||||||
 | 
					                    lon: options.location[1],
 | 
				
			||||||
 | 
					                    zoom: zoom
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                if (args[1]) {
 | 
				
			||||||
 | 
					                    // We have a prefered map!
 | 
				
			||||||
 | 
					                    options.mapBackgroundLayer = ValidatedTextField.bestLayerAt(
 | 
				
			||||||
 | 
					                        location, new UIEventSource<string[]>(args[1].split(","))
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                const li = new LengthInput(options.mapBackgroundLayer, location, value)
 | 
				
			||||||
 | 
					                li.SetStyle("height: 20rem;")
 | 
				
			||||||
 | 
					                return li;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
        ValidatedTextField.tp(
 | 
					        ValidatedTextField.tp(
 | 
				
			||||||
            "wikidata",
 | 
					            "wikidata",
 | 
				
			||||||
            "A wikidata identifier, e.g. Q42",
 | 
					            "A wikidata identifier, e.g. Q42",
 | 
				
			||||||
| 
						 | 
					@ -113,22 +194,6 @@ export default class ValidatedTextField {
 | 
				
			||||||
            undefined,
 | 
					            undefined,
 | 
				
			||||||
            undefined,
 | 
					            undefined,
 | 
				
			||||||
            "numeric"),
 | 
					            "numeric"),
 | 
				
			||||||
        ValidatedTextField.tp(
 | 
					 | 
				
			||||||
            "direction",
 | 
					 | 
				
			||||||
            "A geographical direction, in degrees. 0° is north, 90° is east, ... Will return a value between 0 (incl) and 360 (excl)",
 | 
					 | 
				
			||||||
            (str) => {
 | 
					 | 
				
			||||||
                str = "" + str;
 | 
					 | 
				
			||||||
                return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) >= 0 && Number(str) <= 360
 | 
					 | 
				
			||||||
            }, str => str,
 | 
					 | 
				
			||||||
            (value, options) => {
 | 
					 | 
				
			||||||
                return new DirectionInput(options.mapBackgroundLayer , new UIEventSource<Loc>({
 | 
					 | 
				
			||||||
                    lat: options.location[0],
 | 
					 | 
				
			||||||
                    lon: options.location[1],
 | 
					 | 
				
			||||||
                    zoom: 19
 | 
					 | 
				
			||||||
                }),value);
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "numeric"
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        ValidatedTextField.tp(
 | 
					        ValidatedTextField.tp(
 | 
				
			||||||
            "float",
 | 
					            "float",
 | 
				
			||||||
            "A decimal",
 | 
					            "A decimal",
 | 
				
			||||||
| 
						 | 
					@ -222,6 +287,7 @@ export default class ValidatedTextField {
 | 
				
			||||||
     * {string (typename) --> TextFieldDef}
 | 
					     * {string (typename) --> TextFieldDef}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static AllTypes = ValidatedTextField.allTypesDict();
 | 
					    public static AllTypes = ValidatedTextField.allTypesDict();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static InputForType(type: string, options?: {
 | 
					    public static InputForType(type: string, options?: {
 | 
				
			||||||
        placeholder?: string | BaseUIElement,
 | 
					        placeholder?: string | BaseUIElement,
 | 
				
			||||||
        value?: UIEventSource<string>,
 | 
					        value?: UIEventSource<string>,
 | 
				
			||||||
| 
						 | 
					@ -233,7 +299,9 @@ export default class ValidatedTextField {
 | 
				
			||||||
        country?: () => string,
 | 
					        country?: () => string,
 | 
				
			||||||
        location?: [number /*lat*/, number /*lon*/],
 | 
					        location?: [number /*lat*/, number /*lon*/],
 | 
				
			||||||
        mapBackgroundLayer?: UIEventSource<any>,
 | 
					        mapBackgroundLayer?: UIEventSource<any>,
 | 
				
			||||||
        unit?: Unit
 | 
					        unit?: Unit,
 | 
				
			||||||
 | 
					        args?: (string | number | boolean)[] // Extra arguments for the inputHelper,
 | 
				
			||||||
 | 
					        feature?: any
 | 
				
			||||||
    }): InputElement<string> {
 | 
					    }): InputElement<string> {
 | 
				
			||||||
        options = options ?? {};
 | 
					        options = options ?? {};
 | 
				
			||||||
        options.placeholder = options.placeholder ?? type;
 | 
					        options.placeholder = options.placeholder ?? type;
 | 
				
			||||||
| 
						 | 
					@ -292,8 +360,7 @@ export default class ValidatedTextField {
 | 
				
			||||||
                (valueWithDenom: string) => {
 | 
					                (valueWithDenom: string) => {
 | 
				
			||||||
                    // Take the value from OSM and feed it into the textfield and the dropdown
 | 
					                    // Take the value from OSM and feed it into the textfield and the dropdown
 | 
				
			||||||
                    const withDenom = unit.findDenomination(valueWithDenom);
 | 
					                    const withDenom = unit.findDenomination(valueWithDenom);
 | 
				
			||||||
                    if(withDenom === undefined)
 | 
					                    if (withDenom === undefined) {
 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        // Not a valid value at all - we give it undefined and leave the details up to the other elements
 | 
					                        // Not a valid value at all - we give it undefined and leave the details up to the other elements
 | 
				
			||||||
                        return [undefined, undefined]
 | 
					                        return [undefined, undefined]
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
| 
						 | 
					@ -308,8 +375,9 @@ export default class ValidatedTextField {
 | 
				
			||||||
        if (tp.inputHelper) {
 | 
					        if (tp.inputHelper) {
 | 
				
			||||||
            const helper = tp.inputHelper(input.GetValue(), {
 | 
					            const helper = tp.inputHelper(input.GetValue(), {
 | 
				
			||||||
                location: options.location,
 | 
					                location: options.location,
 | 
				
			||||||
                mapBackgroundLayer: options.mapBackgroundLayer
 | 
					                mapBackgroundLayer: options.mapBackgroundLayer,
 | 
				
			||||||
 | 
					                args: options.args,
 | 
				
			||||||
 | 
					                feature: options.feature
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            input = new CombinedInputElement(input, helper,
 | 
					            input = new CombinedInputElement(input, helper,
 | 
				
			||||||
                (a, _) => a, // We can ignore b, as they are linked earlier
 | 
					                (a, _) => a, // We can ignore b, as they are linked earlier
 | 
				
			||||||
| 
						 | 
					@ -318,6 +386,7 @@ export default class ValidatedTextField {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return input;
 | 
					        return input;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static HelpText(): string {
 | 
					    public static HelpText(): string {
 | 
				
			||||||
        const explanations = ValidatedTextField.tpList.map(type => ["## " + type.name, "", type.explanation].join("\n")).join("\n\n")
 | 
					        const explanations = ValidatedTextField.tpList.map(type => ["## " + type.name, "", type.explanation].join("\n")).join("\n\n")
 | 
				
			||||||
        return "# Available types for text fields\n\nThe listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them\n\n" + explanations
 | 
					        return "# Available types for text fields\n\nThe listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them\n\n" + explanations
 | 
				
			||||||
| 
						 | 
					@ -329,7 +398,9 @@ export default class ValidatedTextField {
 | 
				
			||||||
                      reformat?: ((s: string, country?: () => string) => string),
 | 
					                      reformat?: ((s: string, country?: () => string) => string),
 | 
				
			||||||
                      inputHelper?: (value: UIEventSource<string>, options?: {
 | 
					                      inputHelper?: (value: UIEventSource<string>, options?: {
 | 
				
			||||||
                          location: [number, number],
 | 
					                          location: [number, number],
 | 
				
			||||||
                          mapBackgroundLayer: UIEventSource<any>
 | 
					                          mapBackgroundLayer: UIEventSource<any>,
 | 
				
			||||||
 | 
					                          args: string[],
 | 
				
			||||||
 | 
					                          feature: any
 | 
				
			||||||
                      }) => InputElement<string>,
 | 
					                      }) => InputElement<string>,
 | 
				
			||||||
                      inputmode?: string): TextFieldDef {
 | 
					                      inputmode?: string): TextFieldDef {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -333,12 +333,15 @@ export default class TagRenderingQuestion extends Combine {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const tagsData = tags.data;
 | 
					        const tagsData = tags.data;
 | 
				
			||||||
 | 
					        const feature = State.state.allElements.ContainingFeatures.get(tagsData.id)
 | 
				
			||||||
        const input: InputElement<string> = ValidatedTextField.InputForType(configuration.freeform.type, {
 | 
					        const input: InputElement<string> = ValidatedTextField.InputForType(configuration.freeform.type, {
 | 
				
			||||||
            isValid: (str) => (str.length <= 255),
 | 
					            isValid: (str) => (str.length <= 255),
 | 
				
			||||||
            country: () => tagsData._country,
 | 
					            country: () => tagsData._country,
 | 
				
			||||||
            location: [tagsData._lat, tagsData._lon],
 | 
					            location: [tagsData._lat, tagsData._lon],
 | 
				
			||||||
            mapBackgroundLayer: State.state.backgroundLayer,
 | 
					            mapBackgroundLayer: State.state.backgroundLayer,
 | 
				
			||||||
            unit: applicableUnit
 | 
					            unit: applicableUnit,
 | 
				
			||||||
 | 
					            args: configuration.freeform.helperArgs,
 | 
				
			||||||
 | 
					            feature: feature
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        input.GetValue().setData(tagsData[freeform.key] ?? freeform.default);
 | 
					        input.GetValue().setData(tagsData[freeform.key] ?? freeform.default);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,7 +39,8 @@ export default class SpecialVisualizations {
 | 
				
			||||||
    static constructMiniMap: (options?: {
 | 
					    static constructMiniMap: (options?: {
 | 
				
			||||||
        background?: UIEventSource<BaseLayer>,
 | 
					        background?: UIEventSource<BaseLayer>,
 | 
				
			||||||
        location?: UIEventSource<Loc>,
 | 
					        location?: UIEventSource<Loc>,
 | 
				
			||||||
        allowMoving?: boolean
 | 
					        allowMoving?: boolean,
 | 
				
			||||||
 | 
					        leafletOptions?: any
 | 
				
			||||||
    }) => BaseUIElement;
 | 
					    }) => BaseUIElement;
 | 
				
			||||||
    static constructShowDataLayer: (features: UIEventSource<{ feature: any; freshness: Date }[]>, leafletMap: UIEventSource<any>, layoutToUse: UIEventSource<any>, enablePopups?: boolean, zoomToFeatures?: boolean) => any;
 | 
					    static constructShowDataLayer: (features: UIEventSource<{ feature: any; freshness: Date }[]>, leafletMap: UIEventSource<any>, layoutToUse: UIEventSource<any>, enablePopups?: boolean, zoomToFeatures?: boolean) => any;
 | 
				
			||||||
    public static specialVisualizations: SpecialVisualization[] =
 | 
					    public static specialVisualizations: SpecialVisualization[] =
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										115
									
								
								assets/svg/length-crosshair.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								assets/svg/length-crosshair.svg
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,115 @@
 | 
				
			||||||
 | 
					<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
				
			||||||
 | 
					<svg
 | 
				
			||||||
 | 
					   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
				
			||||||
 | 
					   xmlns:cc="http://creativecommons.org/ns#"
 | 
				
			||||||
 | 
					   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
				
			||||||
 | 
					   xmlns:svg="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
					   xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
					   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
				
			||||||
 | 
					   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
				
			||||||
 | 
					   version="1.0"
 | 
				
			||||||
 | 
					   width="859.53607pt"
 | 
				
			||||||
 | 
					   height="858.4754pt"
 | 
				
			||||||
 | 
					   viewBox="0 0 859.53607 858.4754"
 | 
				
			||||||
 | 
					   preserveAspectRatio="xMidYMid meet"
 | 
				
			||||||
 | 
					   id="svg14"
 | 
				
			||||||
 | 
					   sodipodi:docname="length-crosshair.svg"
 | 
				
			||||||
 | 
					   inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
 | 
				
			||||||
 | 
					  <defs
 | 
				
			||||||
 | 
					     id="defs18" />
 | 
				
			||||||
 | 
					  <sodipodi:namedview
 | 
				
			||||||
 | 
					     pagecolor="#ffffff"
 | 
				
			||||||
 | 
					     bordercolor="#666666"
 | 
				
			||||||
 | 
					     borderopacity="1"
 | 
				
			||||||
 | 
					     objecttolerance="10"
 | 
				
			||||||
 | 
					     gridtolerance="10"
 | 
				
			||||||
 | 
					     guidetolerance="10"
 | 
				
			||||||
 | 
					     inkscape:pageopacity="0"
 | 
				
			||||||
 | 
					     inkscape:pageshadow="2"
 | 
				
			||||||
 | 
					     inkscape:window-width="1920"
 | 
				
			||||||
 | 
					     inkscape:window-height="999"
 | 
				
			||||||
 | 
					     id="namedview16"
 | 
				
			||||||
 | 
					     showgrid="false"
 | 
				
			||||||
 | 
					     showguides="true"
 | 
				
			||||||
 | 
					     inkscape:guide-bbox="true"
 | 
				
			||||||
 | 
					     inkscape:zoom="0.5"
 | 
				
			||||||
 | 
					     inkscape:cx="307.56567"
 | 
				
			||||||
 | 
					     inkscape:cy="-35.669379"
 | 
				
			||||||
 | 
					     inkscape:window-x="0"
 | 
				
			||||||
 | 
					     inkscape:window-y="0"
 | 
				
			||||||
 | 
					     inkscape:window-maximized="1"
 | 
				
			||||||
 | 
					     inkscape:current-layer="svg14"
 | 
				
			||||||
 | 
					     inkscape:snap-smooth-nodes="true" />
 | 
				
			||||||
 | 
					  <metadata
 | 
				
			||||||
 | 
					     id="metadata2">
 | 
				
			||||||
 | 
					Created by potrace 1.15, written by Peter Selinger 2001-2017
 | 
				
			||||||
 | 
					<rdf:RDF>
 | 
				
			||||||
 | 
					  <cc:Work
 | 
				
			||||||
 | 
					     rdf:about="">
 | 
				
			||||||
 | 
					    <dc:format>image/svg+xml</dc:format>
 | 
				
			||||||
 | 
					    <dc:type
 | 
				
			||||||
 | 
					       rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
				
			||||||
 | 
					    <dc:title />
 | 
				
			||||||
 | 
					  </cc:Work>
 | 
				
			||||||
 | 
					</rdf:RDF>
 | 
				
			||||||
 | 
					</metadata>
 | 
				
			||||||
 | 
					  <path
 | 
				
			||||||
 | 
					     style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:2.99999994;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:71.99999853,71.99999853;stroke-dashoffset:0;stroke-opacity:1"
 | 
				
			||||||
 | 
					     id="path816"
 | 
				
			||||||
 | 
					     transform="rotate(-89.47199)"
 | 
				
			||||||
 | 
					     sodipodi:type="arc"
 | 
				
			||||||
 | 
					     sodipodi:cx="-425.24921"
 | 
				
			||||||
 | 
					     sodipodi:cy="433.71375"
 | 
				
			||||||
 | 
					     sodipodi:rx="428.34982"
 | 
				
			||||||
 | 
					     sodipodi:ry="427.81949"
 | 
				
			||||||
 | 
					     sodipodi:start="0"
 | 
				
			||||||
 | 
					     sodipodi:end="4.7117019"
 | 
				
			||||||
 | 
					     sodipodi:open="true"
 | 
				
			||||||
 | 
					     d="M 3.1006165,433.71375 A 428.34982,427.81949 0 0 1 -425.1511,861.53322 428.34982,427.81949 0 0 1 -853.59898,433.90971 428.34982,427.81949 0 0 1 -425.54352,5.8943576" />
 | 
				
			||||||
 | 
					  <path
 | 
				
			||||||
 | 
					     style="fill:none;stroke:#000000;stroke-width:4.49999991;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
				
			||||||
 | 
					     d="m 429.76804,430.08754 0,-429.19968"
 | 
				
			||||||
 | 
					     id="path820"
 | 
				
			||||||
 | 
					     inkscape:connector-curvature="0" />
 | 
				
			||||||
 | 
					  <path
 | 
				
			||||||
 | 
					     style="fill:none;stroke:#000000;stroke-width:1.49999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:35.99999926,35.99999926;stroke-dashoffset:0"
 | 
				
			||||||
 | 
					     d="m 857.58749,429.23771 -855.6389371,0 v 0"
 | 
				
			||||||
 | 
					     id="path822"
 | 
				
			||||||
 | 
					     inkscape:connector-curvature="0" />
 | 
				
			||||||
 | 
					  <path
 | 
				
			||||||
 | 
					     inkscape:connector-curvature="0"
 | 
				
			||||||
 | 
					     id="path814"
 | 
				
			||||||
 | 
					     d="M 429.76804,857.30628 V 428.78674"
 | 
				
			||||||
 | 
					     style="fill:none;stroke:#000000;stroke-width:1.49999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:35.99999926,35.99999926;stroke-dashoffset:0" />
 | 
				
			||||||
 | 
					  <path
 | 
				
			||||||
 | 
					     inkscape:connector-curvature="0"
 | 
				
			||||||
 | 
					     id="path826"
 | 
				
			||||||
 | 
					     d="M 857.32232,1.0332137 H 1.6833879 v 0"
 | 
				
			||||||
 | 
					     style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:17.99999963, 17.99999963;stroke-dashoffset:0;stroke-opacity:1" />
 | 
				
			||||||
 | 
					  <path
 | 
				
			||||||
 | 
					     inkscape:connector-curvature="0"
 | 
				
			||||||
 | 
					     id="path828"
 | 
				
			||||||
 | 
					     d="M 857.58749,858.2377 H 1.9485529 v 0"
 | 
				
			||||||
 | 
					     style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:8.99999982, 8.99999982;stroke-dashoffset:0;stroke-opacity:1" />
 | 
				
			||||||
 | 
					  <path
 | 
				
			||||||
 | 
					     cx="-429.2377"
 | 
				
			||||||
 | 
					     cy="429.76804"
 | 
				
			||||||
 | 
					     rx="428.34982"
 | 
				
			||||||
 | 
					     ry="427.81949"
 | 
				
			||||||
 | 
					     transform="rotate(-90)"
 | 
				
			||||||
 | 
					     id="path825"
 | 
				
			||||||
 | 
					     style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:11.99999975;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
 | 
				
			||||||
 | 
					  <path
 | 
				
			||||||
 | 
					     d="M -5.3639221,-424.71887 A 428.34982,427.81949 0 0 1 -429.83855,3.0831087 428.34982,427.81949 0 0 1 -861.99345,-416.97839"
 | 
				
			||||||
 | 
					     sodipodi:open="true"
 | 
				
			||||||
 | 
					     sodipodi:end="3.1234988"
 | 
				
			||||||
 | 
					     sodipodi:start="0"
 | 
				
			||||||
 | 
					     sodipodi:ry="427.81949"
 | 
				
			||||||
 | 
					     sodipodi:rx="428.34982"
 | 
				
			||||||
 | 
					     sodipodi:cy="-424.71887"
 | 
				
			||||||
 | 
					     sodipodi:cx="-433.71375"
 | 
				
			||||||
 | 
					     sodipodi:type="arc"
 | 
				
			||||||
 | 
					     transform="rotate(-179.47199)"
 | 
				
			||||||
 | 
					     id="path827"
 | 
				
			||||||
 | 
					     style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:4.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
 | 
				
			||||||
 | 
					</svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 4.7 KiB  | 
| 
						 | 
					@ -64,7 +64,13 @@
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "tagRenderings": [
 | 
					      "tagRenderings": [
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          "render": "Deze straat is <b>{width:carriageway}m</b> breed"
 | 
					          "render": "Deze straat is <b>{width:carriageway}m</b> breed",
 | 
				
			||||||
 | 
					          "question": "Hoe breed is deze straat?",
 | 
				
			||||||
 | 
					          "freeform": {
 | 
				
			||||||
 | 
					            "key": "width:carriageway",
 | 
				
			||||||
 | 
					            "type": "length",
 | 
				
			||||||
 | 
					            "helperArgs": [21, "map"]
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          "render": "Deze straat heeft <span class='alert'>{_width:difference}m</span> te weinig:",
 | 
					          "render": "Deze straat heeft <span class='alert'>{_width:difference}m</span> te weinig:",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								index.ts
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								index.ts
									
										
									
									
									
								
							| 
						 | 
					@ -19,10 +19,13 @@ import DirectionInput from "./UI/Input/DirectionInput";
 | 
				
			||||||
import SpecialVisualizations from "./UI/SpecialVisualizations";
 | 
					import SpecialVisualizations from "./UI/SpecialVisualizations";
 | 
				
			||||||
import ShowDataLayer from "./UI/ShowDataLayer";
 | 
					import ShowDataLayer from "./UI/ShowDataLayer";
 | 
				
			||||||
import * as L from "leaflet";
 | 
					import * as L from "leaflet";
 | 
				
			||||||
 | 
					import ValidatedTextField from "./UI/Input/ValidatedTextField";
 | 
				
			||||||
 | 
					import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Workaround for a stupid crash: inject some functions which would give stupid circular dependencies or crash the other nodejs scripts
 | 
					// Workaround for a stupid crash: inject some functions which would give stupid circular dependencies or crash the other nodejs scripts
 | 
				
			||||||
SimpleMetaTagger.coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/");
 | 
					SimpleMetaTagger.coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/");
 | 
				
			||||||
DirectionInput.constructMinimap = options =>  new Minimap(options)
 | 
					DirectionInput.constructMinimap = options =>  new Minimap(options)
 | 
				
			||||||
 | 
					ValidatedTextField.bestLayerAt = (location, layerPref) => AvailableBaseLayers.SelectBestLayerAccordingTo(location, layerPref) 
 | 
				
			||||||
SpecialVisualizations.constructMiniMap = options => new Minimap(options)
 | 
					SpecialVisualizations.constructMiniMap = options => new Minimap(options)
 | 
				
			||||||
SpecialVisualizations.constructShowDataLayer = (features: UIEventSource<{ feature: any, freshness: Date }[]>,
 | 
					SpecialVisualizations.constructShowDataLayer = (features: UIEventSource<{ feature: any, freshness: Date }[]>,
 | 
				
			||||||
                                                 leafletMap: UIEventSource<L.Map>,
 | 
					                                                 leafletMap: UIEventSource<L.Map>,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -487,6 +487,11 @@
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "presets": {
 | 
				
			||||||
 | 
					            "0": {
 | 
				
			||||||
 | 
					                "title": "Обслуживание велосипедов/магазин"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "defibrillator": {
 | 
					    "defibrillator": {
 | 
				
			||||||
| 
						 | 
					@ -1064,6 +1069,7 @@
 | 
				
			||||||
            "1": {
 | 
					            "1": {
 | 
				
			||||||
                "question": "Вы хотите добавить описание?"
 | 
					                "question": "Вы хотите добавить описание?"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
 | 
					        "name": "Смотровая площадка"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -122,8 +122,10 @@
 | 
				
			||||||
            "thanksForSharing": "Obrigado por compartilhar!",
 | 
					            "thanksForSharing": "Obrigado por compartilhar!",
 | 
				
			||||||
            "copiedToClipboard": "Link copiado para a área de transferência",
 | 
					            "copiedToClipboard": "Link copiado para a área de transferência",
 | 
				
			||||||
            "addToHomeScreen": "<h3>Adicionar à sua tela inicial</h3>Você pode adicionar facilmente este site à tela inicial do smartphone para uma sensação nativa. Clique no botão 'adicionar à tela inicial' na barra de URL para fazer isso.",
 | 
					            "addToHomeScreen": "<h3>Adicionar à sua tela inicial</h3>Você pode adicionar facilmente este site à tela inicial do smartphone para uma sensação nativa. Clique no botão 'adicionar à tela inicial' na barra de URL para fazer isso.",
 | 
				
			||||||
            "intro": "<h3>Compartilhe este mapa</h3> Compartilhe este mapa copiando o link abaixo e enviando-o para amigos e familiares:"
 | 
					            "intro": "<h3>Compartilhe este mapa</h3> Compartilhe este mapa copiando o link abaixo e enviando-o para amigos e familiares:",
 | 
				
			||||||
        }
 | 
					            "embedIntro": "<h3>Incorpore em seu site</h3>Por favor, incorpore este mapa em seu site.<br>Nós o encorajamos a fazer isso - você nem precisa pedir permissão.<br>É gratuito e sempre será. Quanto mais pessoas usarem isso, mais valioso se tornará."
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "aboutMapcomplete": "<h3>Sobre o MapComplete</h3><p>Com o MapComplete, você pode enriquecer o OpenStreetMap com informações sobre um<b>único tema.</b>Responda a algumas perguntas e, em minutos, suas contribuições estarão disponíveis em todo o mundo! O<b>mantenedor do tema</b>define elementos, questões e linguagens para o tema.</p><h3>Saiba mais</h3><p>MapComplete sempre<b>oferece a próxima etapa</b>para saber mais sobre o OpenStreetMap.</p><ul><li>Quando incorporado em um site, o iframe vincula-se a um MapComplete em tela inteira</li><li>A versão em tela inteira oferece informações sobre o OpenStreetMap</li><li>A visualização funciona sem login, mas a edição requer um login do OSM.</li><li>Se você não estiver conectado, será solicitado que você faça o login</li><li>Depois de responder a uma única pergunta, você pode adicionar novos aponta para o mapa </li><li> Depois de um tempo, as tags OSM reais são mostradas, posteriormente vinculadas ao wiki </li></ul><p></p><br><p>Você percebeu<b>um problema</b>? Você tem uma<b>solicitação de recurso </b>? Quer<b>ajudar a traduzir</b>? Acesse <a href=\"https://github.com/pietervdvn/MapComplete\" target=\"_blank\">o código-fonte</a>ou <a href=\"https: //github.com/pietervdvn/MapComplete / issues \" target=\" _ blank \">rastreador de problemas.</a></p><p>Quer ver<b>seu progresso</b>? Siga a contagem de edição em<a href=\"https://osmcha.org/?filters=%7B%22date__gte%22%3A%5B%7B%22label%22%3A%222021-01-01%22%2C%22value%22%3A%222021-01-01%22%7D%5D%2C%22editor%22%3A%5B%7B%22label%22%3A%22mapcomplete%22%2C%22value%22%3A%22mapcomplete%22%7D%5D%7D\" target=\"_blank\">OsmCha</a>.</p>"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "index": {
 | 
					    "index": {
 | 
				
			||||||
        "pickTheme": "Escolha um tema abaixo para começar.",
 | 
					        "pickTheme": "Escolha um tema abaixo para começar.",
 | 
				
			||||||
| 
						 | 
					@ -142,10 +144,13 @@
 | 
				
			||||||
        "no_reviews_yet": "Não há comentários ainda. Seja o primeiro a escrever um e ajude a abrir os dados e os negócios!",
 | 
					        "no_reviews_yet": "Não há comentários ainda. Seja o primeiro a escrever um e ajude a abrir os dados e os negócios!",
 | 
				
			||||||
        "name_required": "É necessário um nome para exibir e criar comentários",
 | 
					        "name_required": "É necessário um nome para exibir e criar comentários",
 | 
				
			||||||
        "title_singular": "Um comentário",
 | 
					        "title_singular": "Um comentário",
 | 
				
			||||||
        "title": "{count} comentários"
 | 
					        "title": "{count} comentários",
 | 
				
			||||||
 | 
					        "tos": "Se você criar um comentário, você concorda com <a href=\"https://mangrove.reviews/terms\" target=\"_blank\"> o TOS e a política de privacidade de Mangrove.reviews </a>",
 | 
				
			||||||
 | 
					        "affiliated_reviewer_warning": "(Revisão de afiliados)"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "favourite": {
 | 
					    "favourite": {
 | 
				
			||||||
        "reload": "Recarregar dados",
 | 
					        "reload": "Recarregar dados",
 | 
				
			||||||
        "panelIntro": "<h3>Seu tema pessoal</h3>Ative suas camadas favoritas de todos os temas oficiais"
 | 
					        "panelIntro": "<h3>Seu tema pessoal</h3>Ative suas camadas favoritas de todos os temas oficiais",
 | 
				
			||||||
 | 
					        "loginNeeded": "<h3>Entrar</h3> Um layout pessoal está disponível apenas para usuários do OpenStreetMap"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,27 @@
 | 
				
			||||||
        "opening_hours": {
 | 
					        "opening_hours": {
 | 
				
			||||||
            "question": "Was sind die Öffnungszeiten von {name}?",
 | 
					            "question": "Was sind die Öffnungszeiten von {name}?",
 | 
				
			||||||
            "render": "<h3>Öffnungszeiten</h3>{opening_hours_table(opening_hours)}"
 | 
					            "render": "<h3>Öffnungszeiten</h3>{opening_hours_table(opening_hours)}"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "level": {
 | 
				
			||||||
 | 
					            "mappings": {
 | 
				
			||||||
 | 
					                "2": {
 | 
				
			||||||
 | 
					                    "then": "Ist im ersten Stock"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "1": {
 | 
				
			||||||
 | 
					                    "then": "Ist im Erdgeschoss"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "render": "Befindet sich im {level}ten Stock",
 | 
				
			||||||
 | 
					            "question": "In welchem Stockwerk befindet sich dieses Objekt?"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "description": {
 | 
				
			||||||
 | 
					            "question": "Gibt es noch etwas, das die vorhergehenden Fragen nicht abgedeckt haben? Hier wäre Platz dafür.<br/><span style='font-size: small'>Bitte keine bereits erhobenen Informationen.</span>"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "website": {
 | 
				
			||||||
 | 
					            "question": "Was ist die Website von {name}?"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "email": {
 | 
				
			||||||
 | 
					            "question": "Was ist die Mail-Adresse von {name}?"
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1 +1,30 @@
 | 
				
			||||||
{}
 | 
					{
 | 
				
			||||||
 | 
					    "undefined": {
 | 
				
			||||||
 | 
					        "level": {
 | 
				
			||||||
 | 
					            "render": "Localizado no {level}o andar",
 | 
				
			||||||
 | 
					            "mappings": {
 | 
				
			||||||
 | 
					                "2": {
 | 
				
			||||||
 | 
					                    "then": "Localizado no primeiro andar"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "1": {
 | 
				
			||||||
 | 
					                    "then": "Localizado no térreo"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "0": {
 | 
				
			||||||
 | 
					                    "then": "Localizado no subsolo"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "opening_hours": {
 | 
				
			||||||
 | 
					            "question": "Qual o horário de funcionamento de {name}?"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "website": {
 | 
				
			||||||
 | 
					            "question": "Qual o site de {name}?"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "email": {
 | 
				
			||||||
 | 
					            "question": "Qual o endereço de e-mail de {name}?"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "phone": {
 | 
				
			||||||
 | 
					            "question": "Qual o número de telefone de {name}?"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,6 +15,20 @@
 | 
				
			||||||
        "opening_hours": {
 | 
					        "opening_hours": {
 | 
				
			||||||
            "question": "Какое время работы у {name}?",
 | 
					            "question": "Какое время работы у {name}?",
 | 
				
			||||||
            "render": "<h3>Часы работы</h3>{opening_hours_table(opening_hours)}"
 | 
					            "render": "<h3>Часы работы</h3>{opening_hours_table(opening_hours)}"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "level": {
 | 
				
			||||||
 | 
					            "mappings": {
 | 
				
			||||||
 | 
					                "2": {
 | 
				
			||||||
 | 
					                    "then": "Расположено на первом этаже"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "1": {
 | 
				
			||||||
 | 
					                    "then": "Расположено на первом этаже"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                "0": {
 | 
				
			||||||
 | 
					                    "then": "Расположено под землей"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "render": "Расположено на {level}ом этаже"
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										76
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										76
									
								
								test.ts
									
										
									
									
									
								
							| 
						 | 
					@ -1,76 +0,0 @@
 | 
				
			||||||
import SplitAction from "./Logic/Osm/Actions/SplitAction";
 | 
					 | 
				
			||||||
import {GeoOperations} from "./Logic/GeoOperations";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const way = {
 | 
					 | 
				
			||||||
    "type": "Feature",
 | 
					 | 
				
			||||||
    "properties": {
 | 
					 | 
				
			||||||
        "highway": "residential",
 | 
					 | 
				
			||||||
        "maxweight": "3.5",
 | 
					 | 
				
			||||||
        "maxweight:conditional": "none @ delivery",
 | 
					 | 
				
			||||||
        "name": "Silsstraat",
 | 
					 | 
				
			||||||
        "_last_edit:contributor": "Jorisbo",
 | 
					 | 
				
			||||||
        "_last_edit:contributor:uid": 1983103,
 | 
					 | 
				
			||||||
        "_last_edit:changeset": 70963524,
 | 
					 | 
				
			||||||
        "_last_edit:timestamp": "2019-06-05T18:20:44Z",
 | 
					 | 
				
			||||||
        "_version_number": 9,
 | 
					 | 
				
			||||||
        "id": "way/23583625"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "geometry": {
 | 
					 | 
				
			||||||
        "type": "LineString",
 | 
					 | 
				
			||||||
        "coordinates": [
 | 
					 | 
				
			||||||
            [
 | 
					 | 
				
			||||||
                4.4889691,
 | 
					 | 
				
			||||||
                51.2049831
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            [
 | 
					 | 
				
			||||||
                4.4895496,
 | 
					 | 
				
			||||||
                51.2047718
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            [
 | 
					 | 
				
			||||||
                4.48966,
 | 
					 | 
				
			||||||
                51.2047147
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            [
 | 
					 | 
				
			||||||
                4.4897439,
 | 
					 | 
				
			||||||
                51.2046548
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            [
 | 
					 | 
				
			||||||
                4.4898162,
 | 
					 | 
				
			||||||
                51.2045921
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            [
 | 
					 | 
				
			||||||
                4.4902997,
 | 
					 | 
				
			||||||
                51.2038418
 | 
					 | 
				
			||||||
            ]
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let splitPoint = {
 | 
					 | 
				
			||||||
    "type": "Feature",
 | 
					 | 
				
			||||||
    "properties": {},
 | 
					 | 
				
			||||||
    "geometry": {
 | 
					 | 
				
			||||||
        "type": "Point",
 | 
					 | 
				
			||||||
        "coordinates": [
 | 
					 | 
				
			||||||
            4.490211009979248,
 | 
					 | 
				
			||||||
            51.2041509326002
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let splitClose = {
 | 
					 | 
				
			||||||
    "type": "Feature",
 | 
					 | 
				
			||||||
    "properties": {},
 | 
					 | 
				
			||||||
    "geometry": {
 | 
					 | 
				
			||||||
        "type": "Point",
 | 
					 | 
				
			||||||
        "coordinates": [
 | 
					 | 
				
			||||||
            4.489563927054405,
 | 
					 | 
				
			||||||
            51.2047546593862
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
// State.state = new State(AllKnownLayouts.allKnownLayouts.get("fietsstraten"));
 | 
					 | 
				
			||||||
// add road to state
 | 
					 | 
				
			||||||
// State.state.allElements.addOrGetElement(way);
 | 
					 | 
				
			||||||
new SplitAction(way).DoSplit([splitPoint, splitClose].map(p => GeoOperations.nearestPoint(way,<[number, number]> p.geometry.coordinates)))
 | 
					 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue