forked from MapComplete/MapComplete
		
	Further refactoring
This commit is contained in:
		
							parent
							
								
									e4a2fd1daf
								
							
						
					
					
						commit
						7a7b34b0fa
					
				
					 17 changed files with 350 additions and 297 deletions
				
			
		| 
						 | 
					@ -7,7 +7,7 @@ import Combine from "./UI/Base/Combine";
 | 
				
			||||||
import {UIElement} from "./UI/UIElement";
 | 
					import {UIElement} from "./UI/UIElement";
 | 
				
			||||||
import {MoreScreen} from "./UI/MoreScreen";
 | 
					import {MoreScreen} from "./UI/MoreScreen";
 | 
				
			||||||
import {FilteredLayer} from "./Logic/FilteredLayer";
 | 
					import {FilteredLayer} from "./Logic/FilteredLayer";
 | 
				
			||||||
import {Basemap} from "./Logic/Leaflet/Basemap";
 | 
					import {Basemap} from "./UI/Basemap";
 | 
				
			||||||
import State from "./State";
 | 
					import State from "./State";
 | 
				
			||||||
import {WelcomeMessage} from "./UI/WelcomeMessage";
 | 
					import {WelcomeMessage} from "./UI/WelcomeMessage";
 | 
				
			||||||
import {LayerSelection} from "./UI/LayerSelection";
 | 
					import {LayerSelection} from "./UI/LayerSelection";
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ import {UIEventSource} from "./Logic/UIEventSource";
 | 
				
			||||||
import {QueryParameters} from "./Logic/Web/QueryParameters";
 | 
					import {QueryParameters} from "./Logic/Web/QueryParameters";
 | 
				
			||||||
import {PersonalLayersPanel} from "./UI/PersonalLayersPanel";
 | 
					import {PersonalLayersPanel} from "./UI/PersonalLayersPanel";
 | 
				
			||||||
import Locale from "./UI/i18n/Locale";
 | 
					import Locale from "./UI/i18n/Locale";
 | 
				
			||||||
import {StrayClickHandler} from "./Logic/Leaflet/StrayClickHandler";
 | 
					import {StrayClickHandler} from "./Logic/Actors/StrayClickHandler";
 | 
				
			||||||
import {SimpleAddUI} from "./UI/SimpleAddUI";
 | 
					import {SimpleAddUI} from "./UI/SimpleAddUI";
 | 
				
			||||||
import {CenterMessageBox} from "./UI/CenterMessageBox";
 | 
					import {CenterMessageBox} from "./UI/CenterMessageBox";
 | 
				
			||||||
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
 | 
					import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
 | 
				
			||||||
| 
						 | 
					@ -25,11 +25,10 @@ import {TagUtils} from "./Logic/Tags";
 | 
				
			||||||
import {UserBadge} from "./UI/UserBadge";
 | 
					import {UserBadge} from "./UI/UserBadge";
 | 
				
			||||||
import {SearchAndGo} from "./UI/SearchAndGo";
 | 
					import {SearchAndGo} from "./UI/SearchAndGo";
 | 
				
			||||||
import {FullScreenMessageBox} from "./UI/FullScreenMessageBoxHandler";
 | 
					import {FullScreenMessageBox} from "./UI/FullScreenMessageBoxHandler";
 | 
				
			||||||
import {GeoLocationHandler} from "./Logic/Leaflet/GeoLocationHandler";
 | 
					import {GeoLocationHandler} from "./Logic/Actors/GeoLocationHandler";
 | 
				
			||||||
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
 | 
					import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
 | 
				
			||||||
import {Utils} from "./Utils";
 | 
					import {Utils} from "./Utils";
 | 
				
			||||||
import BackgroundSelector from "./UI/BackgroundSelector";
 | 
					import BackgroundSelector from "./UI/BackgroundSelector";
 | 
				
			||||||
import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
 | 
					 | 
				
			||||||
import {FeatureInfoBox} from "./UI/Popup/FeatureInfoBox";
 | 
					import {FeatureInfoBox} from "./UI/Popup/FeatureInfoBox";
 | 
				
			||||||
import Svg from "./Svg";
 | 
					import Svg from "./Svg";
 | 
				
			||||||
import Link from "./UI/Base/Link";
 | 
					import Link from "./UI/Base/Link";
 | 
				
			||||||
| 
						 | 
					@ -44,40 +43,6 @@ import Constants from "./Models/Constants";
 | 
				
			||||||
export class InitUiElements {
 | 
					export class InitUiElements {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static setupAllLayerElements() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // ------------- Setup the layers -------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        InitUiElements.InitLayers();
 | 
					 | 
				
			||||||
        InitUiElements.InitLayerSelection();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // ------------------ Setup various other UI elements ------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        InitUiElements.OnlyIf(State.state.featureSwitchAddNew, () => {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            let presetCount = 0;
 | 
					 | 
				
			||||||
            for (const layer of State.state.filteredLayers.data) {
 | 
					 | 
				
			||||||
                for (const preset of layer.layerDef.presets) {
 | 
					 | 
				
			||||||
                    presetCount++;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (presetCount == 0) {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            new StrayClickHandler(() => {
 | 
					 | 
				
			||||||
                    return new SimpleAddUI();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        new CenterMessageBox().AttachTo("centermessage");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static InitAll(layoutToUse: LayoutConfig, layoutFromBase64: string, testing: UIEventSource<string>, layoutName: string,
 | 
					    static InitAll(layoutToUse: LayoutConfig, layoutFromBase64: string, testing: UIEventSource<string>, layoutName: string,
 | 
				
			||||||
                   layoutDefinition: string = "") {
 | 
					                   layoutDefinition: string = "") {
 | 
				
			||||||
        if (layoutToUse === undefined) {
 | 
					        if (layoutToUse === undefined) {
 | 
				
			||||||
| 
						 | 
					@ -88,6 +53,8 @@ export class InitUiElements {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        console.log("Using layout: ", layoutToUse.id, "LayoutFromBase64 is ", layoutFromBase64);
 | 
					        console.log("Using layout: ", layoutToUse.id, "LayoutFromBase64 is ", layoutFromBase64);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        State.state = new State(layoutToUse);
 | 
					        State.state = new State(layoutToUse);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // This 'leaks' the global state via the window object, useful for debugging
 | 
					        // This 'leaks' the global state via the window object, useful for debugging
 | 
				
			||||||
| 
						 | 
					@ -116,7 +83,7 @@ export class InitUiElements {
 | 
				
			||||||
        InitUiElements.setupAllLayerElements();
 | 
					        InitUiElements.setupAllLayerElements();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (layoutToUse.customCss !== undefined) {
 | 
					        if (layoutToUse.customCss !== undefined) {
 | 
				
			||||||
           Utils.LoadCustomCss(layoutToUse.customCss);
 | 
					            Utils.LoadCustomCss(layoutToUse.customCss);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function updateFavs() {
 | 
					        function updateFavs() {
 | 
				
			||||||
| 
						 | 
					@ -177,7 +144,7 @@ export class InitUiElements {
 | 
				
			||||||
                        continue;
 | 
					                        continue;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if((layer.title ?? null) === null && layer.tagRenderings.length === 0){
 | 
					                    if ((layer.title ?? null) === null && layer.tagRenderings.length === 0) {
 | 
				
			||||||
                        continue;
 | 
					                        continue;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -231,10 +198,14 @@ export class InitUiElements {
 | 
				
			||||||
                    iconAnchor: [15, 15]
 | 
					                    iconAnchor: [15, 15]
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
                const marker = L.marker([home.lat, home.lon], {icon: icon})
 | 
					                const marker = L.marker([home.lat, home.lon], {icon: icon})
 | 
				
			||||||
                marker.addTo(State.state.bm.map)
 | 
					                marker.addTo(State.state.leafletMap.data)
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        new GeoLocationHandler()
 | 
					        new GeoLocationHandler(
 | 
				
			||||||
 | 
					            State.state.currentGPSLocation,
 | 
				
			||||||
 | 
					            State.state.leafletMap,
 | 
				
			||||||
 | 
					            State.state.featureSwitchGeolocation
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
            .SetStyle(`position:relative;display:block;border: solid 2px #0005;cursor: pointer; z-index: 999; /*Just below leaflets zoom*/background-color: white;border-radius: 5px;width: 43px;height: 43px;`)
 | 
					            .SetStyle(`position:relative;display:block;border: solid 2px #0005;cursor: pointer; z-index: 999; /*Just below leaflets zoom*/background-color: white;border-radius: 5px;width: 43px;height: 43px;`)
 | 
				
			||||||
            .AttachTo("geolocate-button");
 | 
					            .AttachTo("geolocate-button");
 | 
				
			||||||
        State.state.locationControl.ping();
 | 
					        State.state.locationControl.ping();
 | 
				
			||||||
| 
						 | 
					@ -282,55 +253,6 @@ export class InitUiElements {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static CreateWelcomePane() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const layoutToUse = State.state.layoutToUse.data;
 | 
					 | 
				
			||||||
        let welcome: UIElement = new WelcomeMessage();
 | 
					 | 
				
			||||||
        if (layoutToUse.id === personal.id) {
 | 
					 | 
				
			||||||
            welcome = new PersonalLayersPanel();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const tabs = [
 | 
					 | 
				
			||||||
            {header: `<img src='${layoutToUse.icon}'>`, content: welcome},
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                header: Svg.osm_logo_img,
 | 
					 | 
				
			||||||
                content: Translations.t.general.openStreetMapIntro as UIElement
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (State.state.featureSwitchShareScreen.data) {
 | 
					 | 
				
			||||||
            tabs.push({header: Svg.share_img, content: new ShareScreen()});
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (State.state.featureSwitchMoreQuests.data) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            tabs.push({
 | 
					 | 
				
			||||||
                header: Svg.add_img,
 | 
					 | 
				
			||||||
                content: new MoreScreen()
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        tabs.push({
 | 
					 | 
				
			||||||
                header: Svg.help    ,
 | 
					 | 
				
			||||||
                content: new VariableUiElement(State.state.osmConnection.userDetails.map(userdetails => {
 | 
					 | 
				
			||||||
                    if (userdetails.csCount < Constants.userJourney.mapCompleteHelpUnlock) {
 | 
					 | 
				
			||||||
                        return ""
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    return new Combine([Translations.t.general.aboutMapcomplete, "<br/>Version "+Constants.vNumber]).Render();
 | 
					 | 
				
			||||||
                }, [Locale.language]))
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return new TabbedComponent(tabs, State.state.welcomeMessageOpenedTab)
 | 
					 | 
				
			||||||
            .ListenTo(State.state.osmConnection.userDetails);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static InitWelcomeMessage() {
 | 
					    static InitWelcomeMessage() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const fullOptions = this.CreateWelcomePane();
 | 
					        const fullOptions = this.CreateWelcomePane();
 | 
				
			||||||
| 
						 | 
					@ -374,25 +296,6 @@ export class InitUiElements {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static GenerateLayerControlPanel() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let layerControlPanel: UIElement = undefined;
 | 
					 | 
				
			||||||
        if (State.state.layoutToUse.data.enableBackgroundLayerSelection) {
 | 
					 | 
				
			||||||
            layerControlPanel = new BackgroundSelector();
 | 
					 | 
				
			||||||
            layerControlPanel.SetStyle("margin:1em");
 | 
					 | 
				
			||||||
            layerControlPanel.onClick(() => {            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (State.state.filteredLayers.data.length > 1) {
 | 
					 | 
				
			||||||
            const layerSelection = new LayerSelection();
 | 
					 | 
				
			||||||
            layerSelection.onClick(() => {
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            layerControlPanel = new Combine([layerSelection, "<br/>", layerControlPanel]);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return layerControlPanel;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static InitLayerSelection() {
 | 
					    static InitLayerSelection() {
 | 
				
			||||||
        InitUiElements.OnlyIf(State.state.featureSwitchLayers, () => {
 | 
					        InitUiElements.OnlyIf(State.state.featureSwitchLayers, () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -435,37 +338,27 @@ export class InitUiElements {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static InitBaseMap() {
 | 
					    static InitBaseMap() {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        const attr = new Attribution(State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, State.state.leafletMap);
 | 
				
			||||||
        const bm = new Basemap("leafletDiv",
 | 
					        const bm = new Basemap("leafletDiv",
 | 
				
			||||||
            State.state.locationControl,
 | 
					            State.state.locationControl,
 | 
				
			||||||
            State.state.backgroundLayer,
 | 
					            State.state.backgroundLayer,
 | 
				
			||||||
            new Attribution(State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, State.state.bm)
 | 
					            State.state.LastClickLocation,
 | 
				
			||||||
 | 
					            attr
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        State.state.bm = bm;
 | 
					        State.state.leafletMap.setData(bm.map);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        bm.map.on("popupclose", () => {
 | 
					        bm.map.on("popupclose", () => {
 | 
				
			||||||
            State.state.selectedElement.setData(undefined)
 | 
					            State.state.selectedElement.setData(undefined)
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        State.state.layerUpdater = new UpdateFromOverpass(State.state);
 | 
					        State.state.layerUpdater = new UpdateFromOverpass(State.state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        State.state.availableBackgroundLayers = new AvailableBaseLayers(State.state.locationControl, State.state.backgroundLayer).availableEditorLayers;
 | 
					 | 
				
			||||||
        const queryParam = QueryParameters.GetQueryParameter("background", State.state.layoutToUse.data.defaultBackgroundId, "The id of the background layer to start with");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        queryParam.addCallbackAndRun((selectedId: string) => {
 | 
					 | 
				
			||||||
            const available = State.state.availableBackgroundLayers.data;
 | 
					 | 
				
			||||||
            for (const layer of available) {
 | 
					 | 
				
			||||||
                if (layer.id === selectedId) {
 | 
					 | 
				
			||||||
                    State.state.backgroundLayer.setData(layer);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        State.state.backgroundLayer.addCallbackAndRun(currentLayer => {
 | 
					 | 
				
			||||||
            queryParam.setData(currentLayer.id);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    static InitLayers() {
 | 
					    static InitLayers() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const flayers: FilteredLayer[] = []
 | 
					        const flayers: FilteredLayer[] = []
 | 
				
			||||||
| 
						 | 
					@ -496,4 +389,111 @@ export class InitUiElements {
 | 
				
			||||||
        State.state.filteredLayers.setData(flayers);
 | 
					        State.state.filteredLayers.setData(flayers);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static setupAllLayerElements() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // ------------- Setup the layers -------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        InitUiElements.InitLayers();
 | 
				
			||||||
 | 
					        InitUiElements.InitLayerSelection();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // ------------------ Setup various other UI elements ------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        InitUiElements.OnlyIf(State.state.featureSwitchAddNew, () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let presetCount = 0;
 | 
				
			||||||
 | 
					            for (const layer of State.state.filteredLayers.data) {
 | 
				
			||||||
 | 
					                for (const preset of layer.layerDef.presets) {
 | 
				
			||||||
 | 
					                    presetCount++;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (presetCount == 0) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            new StrayClickHandler(
 | 
				
			||||||
 | 
					                State.state.LastClickLocation,
 | 
				
			||||||
 | 
					                State.state.selectedElement,
 | 
				
			||||||
 | 
					                State.state.filteredLayers,
 | 
				
			||||||
 | 
					                State.state.leafletMap,
 | 
				
			||||||
 | 
					                State.state.fullScreenMessage,
 | 
				
			||||||
 | 
					                () => {
 | 
				
			||||||
 | 
					                    return new SimpleAddUI();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        new CenterMessageBox().AttachTo("centermessage");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static CreateWelcomePane() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const layoutToUse = State.state.layoutToUse.data;
 | 
				
			||||||
 | 
					        let welcome: UIElement = new WelcomeMessage();
 | 
				
			||||||
 | 
					        if (layoutToUse.id === personal.id) {
 | 
				
			||||||
 | 
					            welcome = new PersonalLayersPanel();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const tabs = [
 | 
				
			||||||
 | 
					            {header: `<img src='${layoutToUse.icon}'>`, content: welcome},
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                header: Svg.osm_logo_img,
 | 
				
			||||||
 | 
					                content: Translations.t.general.openStreetMapIntro as UIElement
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (State.state.featureSwitchShareScreen.data) {
 | 
				
			||||||
 | 
					            tabs.push({header: Svg.share_img, content: new ShareScreen()});
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (State.state.featureSwitchMoreQuests.data) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            tabs.push({
 | 
				
			||||||
 | 
					                header: Svg.add_img,
 | 
				
			||||||
 | 
					                content: new MoreScreen()
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tabs.push({
 | 
				
			||||||
 | 
					                header: Svg.help,
 | 
				
			||||||
 | 
					                content: new VariableUiElement(State.state.osmConnection.userDetails.map(userdetails => {
 | 
				
			||||||
 | 
					                    if (userdetails.csCount < Constants.userJourney.mapCompleteHelpUnlock) {
 | 
				
			||||||
 | 
					                        return ""
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return new Combine([Translations.t.general.aboutMapcomplete, "<br/>Version " + Constants.vNumber]).Render();
 | 
				
			||||||
 | 
					                }, [Locale.language]))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new TabbedComponent(tabs, State.state.welcomeMessageOpenedTab)
 | 
				
			||||||
 | 
					            .ListenTo(State.state.osmConnection.userDetails);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static GenerateLayerControlPanel() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let layerControlPanel: UIElement = undefined;
 | 
				
			||||||
 | 
					        if (State.state.layoutToUse.data.enableBackgroundLayerSelection) {
 | 
				
			||||||
 | 
					            layerControlPanel = new BackgroundSelector();
 | 
				
			||||||
 | 
					            layerControlPanel.SetStyle("margin:1em");
 | 
				
			||||||
 | 
					            layerControlPanel.onClick(() => {
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (State.state.filteredLayers.data.length > 1) {
 | 
				
			||||||
 | 
					            const layerSelection = new LayerSelection();
 | 
				
			||||||
 | 
					            layerSelection.onClick(() => {
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            layerControlPanel = new Combine([layerSelection, "<br/>", layerControlPanel]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return layerControlPanel;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,9 @@
 | 
				
			||||||
import * as editorlayerindex from "../../assets/editor-layer-index.json"
 | 
					import * as editorlayerindex from "../../assets/editor-layer-index.json"
 | 
				
			||||||
 | 
					import {BaseLayer} from "../../Models/BaseLayer";
 | 
				
			||||||
 | 
					import * as L from "leaflet";
 | 
				
			||||||
 | 
					import * as X from "leaflet-providers";
 | 
				
			||||||
import {UIEventSource} from "../UIEventSource";
 | 
					import {UIEventSource} from "../UIEventSource";
 | 
				
			||||||
import {GeoOperations} from "../GeoOperations";
 | 
					import {GeoOperations} from "../GeoOperations";
 | 
				
			||||||
import {Basemap} from "../Leaflet/Basemap";
 | 
					 | 
				
			||||||
import {BaseLayer} from "../../Models/BaseLayer";
 | 
					 | 
				
			||||||
import * as X from "leaflet-providers";
 | 
					 | 
				
			||||||
import * as L from "leaflet";
 | 
					 | 
				
			||||||
import {TileLayer} from "leaflet";
 | 
					import {TileLayer} from "leaflet";
 | 
				
			||||||
import {Utils} from "../../Utils";
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,12 +31,16 @@ export default class AvailableBaseLayers {
 | 
				
			||||||
    public static layerOverview = AvailableBaseLayers.LoadRasterIndex().concat(AvailableBaseLayers.LoadProviderIndex());
 | 
					    public static layerOverview = AvailableBaseLayers.LoadRasterIndex().concat(AvailableBaseLayers.LoadProviderIndex());
 | 
				
			||||||
    public availableEditorLayers: UIEventSource<BaseLayer[]>;
 | 
					    public availableEditorLayers: UIEventSource<BaseLayer[]>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(location: UIEventSource<{ lat: number, lon: number, zoom: number }>,
 | 
					    constructor(location: UIEventSource<{ lat: number, lon: number, zoom: number }>) {
 | 
				
			||||||
                currentBackgroundLayer: UIEventSource<BaseLayer>) {
 | 
					 | 
				
			||||||
        const self = this;
 | 
					        const self = this;
 | 
				
			||||||
        this.availableEditorLayers =
 | 
					        this.availableEditorLayers =
 | 
				
			||||||
            location.map(
 | 
					            location.map(
 | 
				
			||||||
                (currentLocation) => {
 | 
					                (currentLocation) => {
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    if(currentLocation === undefined){
 | 
				
			||||||
 | 
					                        return AvailableBaseLayers.layerOverview;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
                    const currentLayers = self.availableEditorLayers?.data;
 | 
					                    const currentLayers = self.availableEditorLayers?.data;
 | 
				
			||||||
                    const newLayers = AvailableBaseLayers.AvailableLayersAt(currentLocation?.lon, currentLocation?.lat);
 | 
					                    const newLayers = AvailableBaseLayers.AvailableLayersAt(currentLocation?.lon, currentLocation?.lat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,36 +60,15 @@ export default class AvailableBaseLayers {
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Change the baselayer back to OSM if we go out of the current range of the layer
 | 
					 | 
				
			||||||
        this.availableEditorLayers.addCallbackAndRun(availableLayers => {
 | 
					 | 
				
			||||||
            const currentLayer = currentBackgroundLayer.data.id;
 | 
					 | 
				
			||||||
            for (const availableLayer of availableLayers) {
 | 
					 | 
				
			||||||
                if (availableLayer.id === currentLayer) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (availableLayer.max_zoom < location.data.zoom) {
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (availableLayer.min_zoom > location.data.zoom) {
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    return; // All good - the current layer still works!
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            // Oops, we panned out of range for this layer!
 | 
					 | 
				
			||||||
            console.log("AvailableBaseLayers-actor: detected that the current bounds aren't sufficient anymore - reverting to OSM standard")
 | 
					 | 
				
			||||||
            layerControl.setData(AvailableBaseLayers.osmCarto);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static AvailableLayersAt(lon: number, lat: number): BaseLayer[] {
 | 
					    private static AvailableLayersAt(lon: number, lat: number): BaseLayer[] {
 | 
				
			||||||
        const availableLayers = [AvailableBaseLayers.osmCarto]
 | 
					        const availableLayers = [AvailableBaseLayers.osmCarto]
 | 
				
			||||||
        const globalLayers = [];
 | 
					        const globalLayers = [];
 | 
				
			||||||
        for (const i in AvailableBaseLayers.layerOverview) {
 | 
					        for (const layerOverviewItem of AvailableBaseLayers.layerOverview) {
 | 
				
			||||||
            const layer = AvailableBaseLayers.layerOverview[i];
 | 
					            const layer = layerOverviewItem;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            if (layer.feature?.geometry === undefined || layer.feature?.geometry === null) {
 | 
					            if (layer.feature?.geometry === undefined || layer.feature?.geometry === null) {
 | 
				
			||||||
                globalLayers.push(layer);
 | 
					                globalLayers.push(layer);
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,7 @@
 | 
				
			||||||
import * as L from "leaflet";
 | 
					import * as L from "leaflet";
 | 
				
			||||||
import {UIEventSource} from "../UIEventSource";
 | 
					import {UIEventSource} from "../UIEventSource";
 | 
				
			||||||
import {UIElement} from "../../UI/UIElement";
 | 
					import {UIElement} from "../../UI/UIElement";
 | 
				
			||||||
import State from "../../State";
 | 
					 | 
				
			||||||
import {Utils} from "../../Utils";
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
import {Basemap} from "./Basemap";
 | 
					 | 
				
			||||||
import Svg from "../../Svg";
 | 
					import Svg from "../../Svg";
 | 
				
			||||||
import {Img} from "../../UI/Img";
 | 
					import {Img} from "../../UI/Img";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,12 +9,20 @@ export class GeoLocationHandler extends UIElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private readonly _isActive: UIEventSource<boolean> = new UIEventSource<boolean>(false);
 | 
					    private readonly _isActive: UIEventSource<boolean> = new UIEventSource<boolean>(false);
 | 
				
			||||||
    private readonly _permission: UIEventSource<string> = new UIEventSource<string>("");
 | 
					    private readonly _permission: UIEventSource<string> = new UIEventSource<string>("");
 | 
				
			||||||
    private _marker: any;
 | 
					    private _marker: L.Marker;
 | 
				
			||||||
    private readonly _hasLocation: UIEventSource<boolean>;
 | 
					    private readonly _hasLocation: UIEventSource<boolean>;
 | 
				
			||||||
 | 
					    private readonly _currentGPSLocation: UIEventSource<{ latlng: any; accuracy: number }>;
 | 
				
			||||||
 | 
					    private readonly _leafletMap: UIEventSource<L.Map>;
 | 
				
			||||||
 | 
					    private readonly _featureSwitch: UIEventSource<boolean>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor() {
 | 
					    constructor(currentGPSLocation: UIEventSource<{ latlng: any; accuracy: number }>,
 | 
				
			||||||
 | 
					                leafletMap: UIEventSource<L.Map>,
 | 
				
			||||||
 | 
					                featureSwitch: UIEventSource<boolean>) {
 | 
				
			||||||
        super(undefined);
 | 
					        super(undefined);
 | 
				
			||||||
        this._hasLocation = State.state.currentGPSLocation.map((location) => location !== undefined);
 | 
					        this._currentGPSLocation = currentGPSLocation;
 | 
				
			||||||
 | 
					        this._leafletMap = leafletMap;
 | 
				
			||||||
 | 
					        this._featureSwitch = featureSwitch;
 | 
				
			||||||
 | 
					        this._hasLocation = currentGPSLocation.map((location) => location !== undefined);
 | 
				
			||||||
        var self = this;
 | 
					        var self = this;
 | 
				
			||||||
        import("../../vendor/Leaflet.AccuratePosition.js").then(() => {
 | 
					        import("../../vendor/Leaflet.AccuratePosition.js").then(() => {
 | 
				
			||||||
            self.init();
 | 
					            self.init();
 | 
				
			||||||
| 
						 | 
					@ -33,11 +39,11 @@ export class GeoLocationHandler extends UIElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function onAccuratePositionProgress(e) {
 | 
					        function onAccuratePositionProgress(e) {
 | 
				
			||||||
            State.state.currentGPSLocation.setData({latlng: e.latlng, accuracy: e.accuracy});
 | 
					            self._currentGPSLocation.setData({latlng: e.latlng, accuracy: e.accuracy});
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function onAccuratePositionFound(e) {
 | 
					        function onAccuratePositionFound(e) {
 | 
				
			||||||
            State.state.currentGPSLocation.setData({latlng: e.latlng, accuracy: e.accuracy});
 | 
					            self._currentGPSLocation.setData({latlng: e.latlng, accuracy: e.accuracy});
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function onAccuratePositionError(e) {
 | 
					        function onAccuratePositionError(e) {
 | 
				
			||||||
| 
						 | 
					@ -45,15 +51,13 @@ export class GeoLocationHandler extends UIElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const bm : Basemap = State.state.bm;
 | 
					        const map = this._leafletMap.data;
 | 
				
			||||||
        const map = bm.map;
 | 
					 | 
				
			||||||
        map.on('accuratepositionprogress', onAccuratePositionProgress);
 | 
					        map.on('accuratepositionprogress', onAccuratePositionProgress);
 | 
				
			||||||
        map.on('accuratepositionfound', onAccuratePositionFound);
 | 
					        map.on('accuratepositionfound', onAccuratePositionFound);
 | 
				
			||||||
        map.on('accuratepositionerror', onAccuratePositionError);
 | 
					        map.on('accuratepositionerror', onAccuratePositionError);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this._currentGPSLocation.addCallback((location) => {
 | 
				
			||||||
        State.state.currentGPSLocation.addCallback((location) => {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const color = getComputedStyle(document.body).getPropertyValue("--catch-detail-color")
 | 
					            const color = getComputedStyle(document.body).getPropertyValue("--catch-detail-color")
 | 
				
			||||||
            const icon = L.icon(
 | 
					            const icon = L.icon(
 | 
				
			||||||
| 
						 | 
					@ -88,7 +92,7 @@ export class GeoLocationHandler extends UIElement {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    InnerRender(): string {
 | 
					    InnerRender(): string {
 | 
				
			||||||
        if(!State.state.featureSwitchGeolocation.data){
 | 
					        if (!this._featureSwitch.data) {
 | 
				
			||||||
            return "";
 | 
					            return "";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -102,16 +106,31 @@ export class GeoLocationHandler extends UIElement {
 | 
				
			||||||
        return Svg.crosshair_img;
 | 
					        return Svg.crosshair_img;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    InnerUpdate(htmlElement: HTMLElement) {
 | 
				
			||||||
 | 
					        super.InnerUpdate(htmlElement);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const self = this;
 | 
				
			||||||
 | 
					        htmlElement.onclick = function () {
 | 
				
			||||||
 | 
					            self.StartGeolocating(19);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        htmlElement.oncontextmenu = function (e) {
 | 
				
			||||||
 | 
					            self.StartGeolocating(15);
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private StartGeolocating(zoomlevel = 19) {
 | 
					    private StartGeolocating(zoomlevel = 19) {
 | 
				
			||||||
        const self = this;
 | 
					        const self = this;
 | 
				
			||||||
        const map = State.state.bm.map;
 | 
					        const map : any = this._leafletMap.data;
 | 
				
			||||||
        if (self._permission.data === "denied") {
 | 
					        if (self._permission.data === "denied") {
 | 
				
			||||||
            return "";
 | 
					            return "";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (State.state.currentGPSLocation.data !== undefined) {
 | 
					        if (this._currentGPSLocation.data !== undefined) {
 | 
				
			||||||
            State.state.bm.map.setView(
 | 
					            this._leafletMap.data.setView(
 | 
				
			||||||
                State.state.currentGPSLocation.data.latlng, zoomlevel
 | 
					                this._currentGPSLocation.data.latlng, zoomlevel
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -140,20 +159,4 @@ export class GeoLocationHandler extends UIElement {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    InnerUpdate(htmlElement: HTMLElement) {
 | 
					 | 
				
			||||||
        super.InnerUpdate(htmlElement);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const self = this;
 | 
					 | 
				
			||||||
        htmlElement.onclick = function () {
 | 
					 | 
				
			||||||
            self.StartGeolocating(19);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        htmlElement.oncontextmenu = function (e) {
 | 
					 | 
				
			||||||
            self.StartGeolocating(15);
 | 
					 | 
				
			||||||
            e.preventDefault();
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										44
									
								
								Logic/Actors/LayerResetter.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								Logic/Actors/LayerResetter.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,44 @@
 | 
				
			||||||
 | 
					import {UIEventSource} from "../UIEventSource";
 | 
				
			||||||
 | 
					import {BaseLayer} from "../../Models/BaseLayer";
 | 
				
			||||||
 | 
					import AvailableBaseLayers from "./AvailableBaseLayers";
 | 
				
			||||||
 | 
					import Loc from "../../Models/Loc";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Sets the current background layer to a layer that is actually available
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export default class LayerResetter {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    constructor( currentBackgroundLayer: UIEventSource<BaseLayer>,
 | 
				
			||||||
 | 
					                 location: UIEventSource<Loc>,
 | 
				
			||||||
 | 
					                 availableLayers: UIEventSource<BaseLayer[]>,
 | 
				
			||||||
 | 
					                 defaultLayerId: UIEventSource<string> = undefined) {
 | 
				
			||||||
 | 
					        defaultLayerId = defaultLayerId ??  new UIEventSource<string>(AvailableBaseLayers.osmCarto.id);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Change the baselayer back to OSM if we go out of the current range of the layer
 | 
				
			||||||
 | 
					        availableLayers.addCallbackAndRun(availableLayers => {
 | 
				
			||||||
 | 
					            let defaultLayer = undefined;
 | 
				
			||||||
 | 
					            const currentLayer = currentBackgroundLayer.data.id;
 | 
				
			||||||
 | 
					            for (const availableLayer of availableLayers) {
 | 
				
			||||||
 | 
					                if (availableLayer.id === currentLayer) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (availableLayer.max_zoom < location.data.zoom) {
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (availableLayer.min_zoom > location.data.zoom) {
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if(availableLayer.id === defaultLayerId.data){
 | 
				
			||||||
 | 
					                        defaultLayer = availableLayer;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return; // All good - the current layer still works!
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // Oops, we panned out of range for this layer!
 | 
				
			||||||
 | 
					            console.log("AvailableBaseLayers-actor: detected that the current bounds aren't sufficient anymore - reverting to OSM standard")
 | 
				
			||||||
 | 
					            currentBackgroundLayer.setData(defaultLayer ?? AvailableBaseLayers.osmCarto);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,9 @@
 | 
				
			||||||
import * as L from "leaflet";
 | 
					import * as L from "leaflet";
 | 
				
			||||||
import {UIElement} from "../../UI/UIElement";
 | 
					import {UIElement} from "../../UI/UIElement";
 | 
				
			||||||
import State from "../../State";
 | 
					 | 
				
			||||||
import {Img} from "../../UI/Img";
 | 
					import {Img} from "../../UI/Img";
 | 
				
			||||||
import Svg from "../../Svg";
 | 
					import Svg from "../../Svg";
 | 
				
			||||||
 | 
					import {UIEventSource} from "../UIEventSource";
 | 
				
			||||||
 | 
					import {FilteredLayer} from "../FilteredLayer";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * The stray-click-hanlders adds a marker to the map if no feature was clicked.
 | 
					 * The stray-click-hanlders adds a marker to the map if no feature was clicked.
 | 
				
			||||||
| 
						 | 
					@ -13,25 +14,29 @@ export class StrayClickHandler {
 | 
				
			||||||
    private _uiToShow: (() => UIElement);
 | 
					    private _uiToShow: (() => UIElement);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(
 | 
					    constructor(
 | 
				
			||||||
 | 
					        lastClickLocation: UIEventSource<{ lat: number, lon:number }>,
 | 
				
			||||||
 | 
					        selectedElement: UIEventSource<string>,
 | 
				
			||||||
 | 
					        filteredLayers: UIEventSource<FilteredLayer[]>,
 | 
				
			||||||
 | 
					        leafletMap: UIEventSource<L.Map>,
 | 
				
			||||||
 | 
					        fullscreenMessage: UIEventSource<UIElement>,
 | 
				
			||||||
        uiToShow: (() => UIElement)) {
 | 
					        uiToShow: (() => UIElement)) {
 | 
				
			||||||
        this._uiToShow = uiToShow;
 | 
					        this._uiToShow = uiToShow;
 | 
				
			||||||
        const self = this;
 | 
					        const self = this;
 | 
				
			||||||
        const map = State.state.bm.map;
 | 
					        filteredLayers.data.forEach((filteredLayer) => {
 | 
				
			||||||
        State.state.filteredLayers.data.forEach((filteredLayer) => {
 | 
					 | 
				
			||||||
            filteredLayer.isDisplayed.addCallback(isEnabled => {
 | 
					            filteredLayer.isDisplayed.addCallback(isEnabled => {
 | 
				
			||||||
                if(isEnabled && self._lastMarker){
 | 
					                if(isEnabled && self._lastMarker && leafletMap.data !== undefined){
 | 
				
			||||||
                    // When a layer is activated, we remove the 'last click location' in order to force the user to reclick
 | 
					                    // When a layer is activated, we remove the 'last click location' in order to force the user to reclick
 | 
				
			||||||
                    // This reclick might be at a location where a feature now appeared...
 | 
					                    // This reclick might be at a location where a feature now appeared...
 | 
				
			||||||
                     map.removeLayer(self._lastMarker);
 | 
					                     leafletMap.data.removeLayer(self._lastMarker);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        State.state.bm.LastClickLocation.addCallback(function (lastClick) {
 | 
					        lastClickLocation.addCallback(function (lastClick) {
 | 
				
			||||||
            State.state.selectedElement.setData(undefined);
 | 
					            selectedElement.setData(undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (self._lastMarker !== undefined) {
 | 
					            if (self._lastMarker !== undefined) {
 | 
				
			||||||
                map.removeLayer(self._lastMarker);
 | 
					                leafletMap.data?.removeLayer(self._lastMarker);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            self._lastMarker = L.marker([lastClick.lat, lastClick.lon], {
 | 
					            self._lastMarker = L.marker([lastClick.lat, lastClick.lon], {
 | 
				
			||||||
                icon: L.icon({
 | 
					                icon: L.icon({
 | 
				
			||||||
| 
						 | 
					@ -43,18 +48,18 @@ export class StrayClickHandler {
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            const uiElement = uiToShow();
 | 
					            const uiElement = uiToShow();
 | 
				
			||||||
            const popup = L.popup().setContent(uiElement.Render());
 | 
					            const popup = L.popup().setContent(uiElement.Render());
 | 
				
			||||||
            self._lastMarker.addTo(map);
 | 
					            self._lastMarker.addTo(leafletMap.data);
 | 
				
			||||||
            self._lastMarker.bindPopup(popup);
 | 
					            self._lastMarker.bindPopup(popup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self._lastMarker.on("click", () => {
 | 
					            self._lastMarker.on("click", () => {
 | 
				
			||||||
                State.state.fullScreenMessage.setData(self._uiToShow());
 | 
					                fullscreenMessage.setData(self._uiToShow());
 | 
				
			||||||
                uiElement.Update();
 | 
					                uiElement.Update();
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        State.state.selectedElement.addCallback(() => {
 | 
					        selectedElement.addCallback(() => {
 | 
				
			||||||
            if (self._lastMarker !== undefined) {
 | 
					            if (self._lastMarker !== undefined) {
 | 
				
			||||||
                map.removeLayer(self._lastMarker);
 | 
					                leafletMap.data.removeLayer(self._lastMarker);
 | 
				
			||||||
                this._lastMarker = undefined;
 | 
					                this._lastMarker = undefined;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Keeps track of a dictionary 'elementID' -> element
 | 
					 * Keeps track of a dictionary 'elementID' -> UIEventSource<tags>
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
import {UIEventSource} from "./UIEventSource";
 | 
					import {UIEventSource} from "./UIEventSource";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,7 +58,7 @@ export class FilteredLayer {
 | 
				
			||||||
            [State.state.locationControl]
 | 
					            [State.state.locationControl]
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        this.combinedIsDisplayed.addCallback(function (isDisplayed) {
 | 
					        this.combinedIsDisplayed.addCallback(function (isDisplayed) {
 | 
				
			||||||
            const map = State.state.bm.map;
 | 
					            const map = State.state.leafletMap.data;
 | 
				
			||||||
            if (self._geolayer !== undefined && self._geolayer !== null) {
 | 
					            if (self._geolayer !== undefined && self._geolayer !== null) {
 | 
				
			||||||
                if (isDisplayed) {
 | 
					                if (isDisplayed) {
 | 
				
			||||||
                    self._geolayer.addTo(map);
 | 
					                    self._geolayer.addTo(map);
 | 
				
			||||||
| 
						 | 
					@ -116,7 +116,7 @@ export class FilteredLayer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (this._geolayer !== undefined && this._geolayer !== null) {
 | 
					        if (this._geolayer !== undefined && this._geolayer !== null) {
 | 
				
			||||||
            // Remove the old geojson layer from the map - we'll reshow all the elements later on anyway
 | 
					            // Remove the old geojson layer from the map - we'll reshow all the elements later on anyway
 | 
				
			||||||
            State.state.bm.map.removeLayer(this._geolayer);
 | 
					            State.state.leafletMap.data.removeLayer(this._geolayer);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // We fetch all the data we have to show:
 | 
					        // We fetch all the data we have to show:
 | 
				
			||||||
| 
						 | 
					@ -200,7 +200,7 @@ export class FilteredLayer {
 | 
				
			||||||
               
 | 
					               
 | 
				
			||||||
                    const center = GeoOperations.centerpoint(feature).geometry.coordinates;
 | 
					                    const center = GeoOperations.centerpoint(feature).geometry.coordinates;
 | 
				
			||||||
                    popup.setLatLng({lat: center[1], lng: center[0]});
 | 
					                    popup.setLatLng({lat: center[1], lng: center[0]});
 | 
				
			||||||
                    popup.openOn(State.state.bm.map);
 | 
					                    popup.openOn(State.state.leafletMap.data);
 | 
				
			||||||
                    State.state.selectedElement.setData(feature);
 | 
					                    State.state.selectedElement.setData(feature);
 | 
				
			||||||
                    uiElement.Update();
 | 
					                    uiElement.Update();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
| 
						 | 
					@ -209,7 +209,7 @@ export class FilteredLayer {
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (this.combinedIsDisplayed.data) {
 | 
					        if (this.combinedIsDisplayed.data) {
 | 
				
			||||||
            this._geolayer.addTo(State.state.bm.map);
 | 
					            this._geolayer.addTo(State.state.leafletMap.data);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ export class Geocoding {
 | 
				
			||||||
    static Search(query: string,
 | 
					    static Search(query: string,
 | 
				
			||||||
                  handleResult: ((places: { display_name: string, lat: number, lon: number, boundingbox: number[] }[]) => void),
 | 
					                  handleResult: ((places: { display_name: string, lat: number, lon: number, boundingbox: number[] }[]) => void),
 | 
				
			||||||
                  onFail: (() => void)) {
 | 
					                  onFail: (() => void)) {
 | 
				
			||||||
        const b = State.state.bm.map.getBounds();
 | 
					        const b = State.state.leafletMap.data.getBounds();
 | 
				
			||||||
        console.log(b);
 | 
					        console.log(b);
 | 
				
			||||||
        $.getJSON(
 | 
					        $.getJSON(
 | 
				
			||||||
            Geocoding.host + "format=json&limit=1&viewbox=" + 
 | 
					            Geocoding.host + "format=json&limit=1&viewbox=" + 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -162,7 +162,7 @@ export class UpdateFromOverpass {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const bounds = state.bm.map.getBounds();
 | 
					        const bounds = state.leafletMap.data.getBounds();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const diff = state.layoutToUse.data.widenFactor;
 | 
					        const diff = state.layoutToUse.data.widenFactor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -196,7 +196,7 @@ export class UpdateFromOverpass {
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const b = state.bm.map.getBounds();
 | 
					        const b = state.leafletMap.data.getBounds();
 | 
				
			||||||
        return b.getSouth() >= bounds.south &&
 | 
					        return b.getSouth() >= bounds.south &&
 | 
				
			||||||
            b.getNorth() <= bounds.north &&
 | 
					            b.getNorth() <= bounds.north &&
 | 
				
			||||||
            b.getEast() <= bounds.east &&
 | 
					            b.getEast() <= bounds.east &&
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										69
									
								
								State.ts
									
										
									
									
									
								
							
							
						
						
									
										69
									
								
								State.ts
									
										
									
									
									
								
							| 
						 | 
					@ -18,6 +18,8 @@ import {BaseLayer} from "./Models/BaseLayer";
 | 
				
			||||||
import Loc from "./Models/Loc";
 | 
					import Loc from "./Models/Loc";
 | 
				
			||||||
import Constants from "./Models/Constants";
 | 
					import Constants from "./Models/Constants";
 | 
				
			||||||
import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
 | 
					import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
 | 
				
			||||||
 | 
					import * as L from "leaflet"
 | 
				
			||||||
 | 
					import LayerResetter from "./Logic/Actors/LayerResetter";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Contains the global state: a bunch of UI-event sources
 | 
					 * Contains the global state: a bunch of UI-event sources
 | 
				
			||||||
| 
						 | 
					@ -41,9 +43,9 @@ export default class State {
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public changes: Changes;
 | 
					    public changes: Changes;
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     THe basemap with leaflet instance
 | 
					     The leaflet instance of the big basemap
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public bm;
 | 
					    public leafletMap = new UIEventSource<L.Map>(undefined);
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Background layer id
 | 
					     * Background layer id
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
| 
						 | 
					@ -91,7 +93,10 @@ export default class State {
 | 
				
			||||||
     * The map location: currently centered lat, lon and zoom
 | 
					     * The map location: currently centered lat, lon and zoom
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public readonly locationControl = new UIEventSource<Loc>(undefined);
 | 
					    public readonly locationControl = new UIEventSource<Loc>(undefined);
 | 
				
			||||||
    public readonly backgroundLayer = new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto);
 | 
					    public readonly backgroundLayer;
 | 
				
			||||||
 | 
					    /* Last location where a click was registered
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public readonly LastClickLocation: UIEventSource<{ lat: number, lon: number }> = new UIEventSource<{ lat: number, lon: number }>(undefined)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * The location as delivered by the GPS
 | 
					     * The location as delivered by the GPS
 | 
				
			||||||
| 
						 | 
					@ -114,23 +119,12 @@ export default class State {
 | 
				
			||||||
        const self = this;
 | 
					        const self = this;
 | 
				
			||||||
        this.layoutToUse.setData(layoutToUse);
 | 
					        this.layoutToUse.setData(layoutToUse);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function asFloat(source: UIEventSource<string>): UIEventSource<number> {
 | 
					        const zoom = State.asFloat(
 | 
				
			||||||
            return source.map(str => {
 | 
					 | 
				
			||||||
                let parsed = parseFloat(str);
 | 
					 | 
				
			||||||
                return isNaN(parsed) ? undefined : parsed;
 | 
					 | 
				
			||||||
            }, [], fl => {
 | 
					 | 
				
			||||||
                if (fl === undefined || isNaN(fl)) {
 | 
					 | 
				
			||||||
                    return undefined;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                return ("" + fl).substr(0, 8);
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        const zoom = asFloat(
 | 
					 | 
				
			||||||
            QueryParameters.GetQueryParameter("z", "" + layoutToUse.startZoom, "The initial/current zoom level")
 | 
					            QueryParameters.GetQueryParameter("z", "" + layoutToUse.startZoom, "The initial/current zoom level")
 | 
				
			||||||
            .syncWith(LocalStorageSource.Get("zoom")));
 | 
					            .syncWith(LocalStorageSource.Get("zoom")));
 | 
				
			||||||
        const lat = asFloat(QueryParameters.GetQueryParameter("lat", "" + layoutToUse.startLat, "The initial/current latitude")
 | 
					        const lat = State.asFloat(QueryParameters.GetQueryParameter("lat", "" + layoutToUse.startLat, "The initial/current latitude")
 | 
				
			||||||
            .syncWith(LocalStorageSource.Get("lat")));
 | 
					            .syncWith(LocalStorageSource.Get("lat")));
 | 
				
			||||||
        const lon = asFloat(QueryParameters.GetQueryParameter("lon", "" + layoutToUse.startLon, "The initial/current longitude of the app")
 | 
					        const lon = State.asFloat(QueryParameters.GetQueryParameter("lon", "" + layoutToUse.startLon, "The initial/current longitude of the app")
 | 
				
			||||||
            .syncWith(LocalStorageSource.Get("lon")));
 | 
					            .syncWith(LocalStorageSource.Get("lon")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -153,11 +147,35 @@ export default class State {
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.availableBackgroundLayers = new AvailableBaseLayers(this.locationControl).availableEditorLayers;
 | 
				
			||||||
 | 
					        this.backgroundLayer = QueryParameters.GetQueryParameter("background",
 | 
				
			||||||
 | 
					            this.layoutToUse.data.defaultBackgroundId ?? AvailableBaseLayers.osmCarto.id,
 | 
				
			||||||
 | 
					            "The id of the background layer to start with")
 | 
				
			||||||
 | 
					            .map((selectedId: string) => {
 | 
				
			||||||
 | 
					                console.log("SELECTED ID", selectedId)
 | 
				
			||||||
 | 
					                const available = self.availableBackgroundLayers.data;
 | 
				
			||||||
 | 
					                for (const layer of available) {
 | 
				
			||||||
 | 
					                    if (layer.id === selectedId) {
 | 
				
			||||||
 | 
					                        return layer;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return AvailableBaseLayers.osmCarto;
 | 
				
			||||||
 | 
					            }, [], layer => layer.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        new LayerResetter(
 | 
				
			||||||
 | 
					            this.backgroundLayer,this.locationControl,
 | 
				
			||||||
 | 
					            this.availableBackgroundLayers, this.layoutToUse.map((layout : LayoutConfig)=> layout.defaultBackgroundId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function featSw(key: string, deflt: (layout: LayoutConfig) => boolean, documentation: string): UIEventSource<boolean> {
 | 
					        function featSw(key: string, deflt: (layout: LayoutConfig) => boolean, documentation: string): UIEventSource<boolean> {
 | 
				
			||||||
            const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined, documentation);
 | 
					            const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined, documentation);
 | 
				
			||||||
            // I'm so sorry about someone trying to decipher this
 | 
					            // I'm so sorry about someone trying to decipher this
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // It takes the current layout, extracts the default value for this query paramter. A query parameter event source is then retreived and flattened
 | 
					            // It takes the current layout, extracts the default value for this query parameter. A query parameter event source is then retrieved and flattened
 | 
				
			||||||
            return UIEventSource.flatten(
 | 
					            return UIEventSource.flatten(
 | 
				
			||||||
                self.layoutToUse.map((layout) => {
 | 
					                self.layoutToUse.map((layout) => {
 | 
				
			||||||
                    const defaultValue = deflt(layout);
 | 
					                    const defaultValue = deflt(layout);
 | 
				
			||||||
| 
						 | 
					@ -186,6 +204,9 @@ export default class State {
 | 
				
			||||||
        this.featureSwitchGeolocation = featSw("fs-geolocation", (layoutToUse) => layoutToUse?.enableGeolocation ?? true,
 | 
					        this.featureSwitchGeolocation = featSw("fs-geolocation", (layoutToUse) => layoutToUse?.enableGeolocation ?? true,
 | 
				
			||||||
            "Disables/Enables the geolocation button");
 | 
					            "Disables/Enables the geolocation button");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        const testParam = QueryParameters.GetQueryParameter("test", "false",
 | 
					        const testParam = QueryParameters.GetQueryParameter("test", "false",
 | 
				
			||||||
            "If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org").data;
 | 
					            "If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org").data;
 | 
				
			||||||
        this.osmConnection = new OsmConnection(
 | 
					        this.osmConnection = new OsmConnection(
 | 
				
			||||||
| 
						 | 
					@ -264,4 +285,16 @@ export default class State {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   private static asFloat(source: UIEventSource<string>): UIEventSource<number> {
 | 
				
			||||||
 | 
					        return source.map(str => {
 | 
				
			||||||
 | 
					            let parsed = parseFloat(str);
 | 
				
			||||||
 | 
					            return isNaN(parsed) ? undefined : parsed;
 | 
				
			||||||
 | 
					        }, [], fl => {
 | 
				
			||||||
 | 
					            if (fl === undefined || isNaN(fl)) {
 | 
				
			||||||
 | 
					                return undefined;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return ("" + fl).substr(0, 8);
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,27 +1,23 @@
 | 
				
			||||||
import * as L from "leaflet"
 | 
					import * as L from "leaflet"
 | 
				
			||||||
import {UIEventSource} from "../UIEventSource";
 | 
					import {UIEventSource} from "../Logic/UIEventSource";
 | 
				
			||||||
import {UIElement} from "../../UI/UIElement";
 | 
					import Loc from "../Models/Loc";
 | 
				
			||||||
import {BaseLayer} from "../../Models/BaseLayer";
 | 
					import {UIElement} from "./UIElement";
 | 
				
			||||||
import AvailableBaseLayers from "../Actors/AvailableBaseLayers";
 | 
					import {BaseLayer} from "../Models/BaseLayer";
 | 
				
			||||||
import Loc from "../../Models/Loc";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Basemap {
 | 
					export class Basemap {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // @ts-ignore
 | 
					    public readonly map: L.Map;
 | 
				
			||||||
    public readonly map: Map;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public readonly LastClickLocation: UIEventSource<{ lat: number, lon: number }> = new UIEventSource<{ lat: number, lon: number }>(undefined)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(leafletElementId: string,
 | 
					    constructor(leafletElementId: string,
 | 
				
			||||||
                location: UIEventSource<Loc>,
 | 
					                location: UIEventSource<Loc>,
 | 
				
			||||||
                currentLayer: UIEventSource<BaseLayer>,
 | 
					                currentLayer: UIEventSource<BaseLayer>,
 | 
				
			||||||
 | 
					                lastClickLocation: UIEventSource<{ lat: number, lon: number }>,
 | 
				
			||||||
                extraAttribution: UIElement) {
 | 
					                extraAttribution: UIElement) {
 | 
				
			||||||
        this.map = L.map(leafletElementId, {
 | 
					        this.map = L.map(leafletElementId, {
 | 
				
			||||||
            center: [location.data.lat ?? 0, location.data.lon ?? 0],
 | 
					            center: [location.data.lat ?? 0, location.data.lon ?? 0],
 | 
				
			||||||
            zoom: location.data.zoom ?? 2,
 | 
					            zoom: location.data.zoom ?? 2,
 | 
				
			||||||
            layers: [AvailableBaseLayers.osmCarto.layer],
 | 
					            layers: [currentLayer.data.layer],
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        L.control.scale(
 | 
					        L.control.scale(
 | 
				
			||||||
| 
						 | 
					@ -64,11 +60,14 @@ export class Basemap {
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.map.on("click", function (e) {
 | 
					        this.map.on("click", function (e) {
 | 
				
			||||||
            self.LastClickLocation.setData({lat: e.latlng.lat, lon: e.latlng.lng})
 | 
					            // @ts-ignore
 | 
				
			||||||
 | 
					            lastClickLocation.setData({lat: e.latlng.lat, lon: e.latlng.lng})
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.map.on("contextmenu", function (e) {
 | 
					        this.map.on("contextmenu", function (e) {
 | 
				
			||||||
            self.LastClickLocation.setData({lat: e.latlng.lat, lon: e.latlng.lng});
 | 
					            // @ts-ignore
 | 
				
			||||||
 | 
					            lastClickLocation.setData({lat: e.latlng.lat, lon: e.latlng.lng});
 | 
				
			||||||
 | 
					            // @ts-ignore
 | 
				
			||||||
            e.preventDefault();
 | 
					            e.preventDefault();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,10 +2,7 @@ import {InputElement} from "./InputElement";
 | 
				
			||||||
import {UIEventSource} from "../../Logic/UIEventSource";
 | 
					import {UIEventSource} from "../../Logic/UIEventSource";
 | 
				
			||||||
import Combine from "../Base/Combine";
 | 
					import Combine from "../Base/Combine";
 | 
				
			||||||
import Svg from "../../Svg";
 | 
					import Svg from "../../Svg";
 | 
				
			||||||
import * as L from "leaflet"
 | 
					
 | 
				
			||||||
import * as X from "leaflet-providers"
 | 
					 | 
				
			||||||
import {Basemap} from "../../Logic/Leaflet/Basemap";
 | 
					 | 
				
			||||||
import State from "../../State";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Selects a direction in degrees
 | 
					 * Selects a direction in degrees
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,30 +1,30 @@
 | 
				
			||||||
import {UIElement} from "../UIElement";
 | 
					import {UIElement} from "../UIElement";
 | 
				
			||||||
import Link from "../Base/Link";
 | 
					import Link from "../Base/Link";
 | 
				
			||||||
import Svg from "../../Svg";
 | 
					import Svg from "../../Svg";
 | 
				
			||||||
import {Basemap} from "../../Logic/Leaflet/Basemap";
 | 
					 | 
				
			||||||
import Combine from "../Base/Combine";
 | 
					import Combine from "../Base/Combine";
 | 
				
			||||||
import {UIEventSource} from "../../Logic/UIEventSource";
 | 
					import {UIEventSource} from "../../Logic/UIEventSource";
 | 
				
			||||||
import {UserDetails} from "../../Logic/Osm/OsmConnection";
 | 
					import {UserDetails} from "../../Logic/Osm/OsmConnection";
 | 
				
			||||||
import Constants from "../../Models/Constants";
 | 
					import Constants from "../../Models/Constants";
 | 
				
			||||||
import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
 | 
					import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
 | 
				
			||||||
import Loc from "../../Models/Loc";
 | 
					import Loc from "../../Models/Loc";
 | 
				
			||||||
 | 
					import * as L from "leaflet"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Attribution extends UIElement {
 | 
					export default class Attribution extends UIElement {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    private readonly _location: UIEventSource<Loc>;
 | 
					    private readonly _location: UIEventSource<Loc>;
 | 
				
			||||||
    private readonly _layoutToUse: UIEventSource<LayoutConfig>;
 | 
					    private readonly _layoutToUse: UIEventSource<LayoutConfig>;
 | 
				
			||||||
    private readonly _userDetails: UIEventSource<UserDetails>;
 | 
					    private readonly _userDetails: UIEventSource<UserDetails>;
 | 
				
			||||||
    private readonly _basemap: Basemap;
 | 
					    private readonly _leafletMap: UIEventSource<L.Map>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(location: UIEventSource<Loc>,
 | 
					    constructor(location: UIEventSource<Loc>,
 | 
				
			||||||
                userDetails: UIEventSource<UserDetails>,
 | 
					                userDetails: UIEventSource<UserDetails>,
 | 
				
			||||||
                layoutToUse: UIEventSource<LayoutConfig>,
 | 
					                layoutToUse: UIEventSource<LayoutConfig>,
 | 
				
			||||||
                basemap: Basemap) {
 | 
					                leafletMap: UIEventSource<L.Map>) {
 | 
				
			||||||
        super(location);
 | 
					        super(location);
 | 
				
			||||||
        this._layoutToUse = layoutToUse;
 | 
					        this._layoutToUse = layoutToUse;
 | 
				
			||||||
        this.ListenTo(layoutToUse);
 | 
					        this.ListenTo(layoutToUse);
 | 
				
			||||||
        this._userDetails = userDetails;
 | 
					        this._userDetails = userDetails;
 | 
				
			||||||
        this._basemap = basemap;
 | 
					        this._leafletMap = leafletMap;
 | 
				
			||||||
        this.ListenTo(userDetails);
 | 
					        this.ListenTo(userDetails);
 | 
				
			||||||
        this._location = location;
 | 
					        this._location = location;
 | 
				
			||||||
        this.SetClass("map-attribution");
 | 
					        this.SetClass("map-attribution");
 | 
				
			||||||
| 
						 | 
					@ -47,9 +47,9 @@ export default class Attribution extends UIElement {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let editWithJosm: (UIElement | string) = ""
 | 
					        let editWithJosm: (UIElement | string) = ""
 | 
				
			||||||
        if (location !== undefined &&
 | 
					        if (location !== undefined &&
 | 
				
			||||||
            this._basemap !== undefined &&
 | 
					            this._leafletMap.data !== undefined &&
 | 
				
			||||||
            userDetails.csCount >=  Constants.userJourney.tagsVisibleAndWikiLinked) {
 | 
					            userDetails.csCount >=  Constants.userJourney.tagsVisibleAndWikiLinked) {
 | 
				
			||||||
            const bounds = this._basemap.map.getBounds();
 | 
					            const bounds = this._leafletMap.data.getBounds();
 | 
				
			||||||
            const top = bounds.getNorth();
 | 
					            const top = bounds.getNorth();
 | 
				
			||||||
            const bottom = bounds.getSouth();
 | 
					            const bottom = bounds.getSouth();
 | 
				
			||||||
            const right = bounds.getEast();
 | 
					            const right = bounds.getEast();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,7 +61,7 @@ export class SearchAndGo extends UIElement {
 | 
				
			||||||
                    [bb[0], bb[2]],
 | 
					                    [bb[0], bb[2]],
 | 
				
			||||||
                    [bb[1], bb[3]]
 | 
					                    [bb[1], bb[3]]
 | 
				
			||||||
                ]
 | 
					                ]
 | 
				
			||||||
                State.state.bm.map.fitBounds(bounds);
 | 
					                State.state.leafletMap.data.fitBounds(bounds);
 | 
				
			||||||
                self._placeholder.setData(Translations.t.general.search.search);
 | 
					                self._placeholder.setData(Translations.t.general.search.search);
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            () => {
 | 
					            () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -115,7 +115,7 @@ export class SimpleAddUI extends UIElement {
 | 
				
			||||||
    private CreatePoint(tags: Tag[], layerToAddTo: FilteredLayer) {
 | 
					    private CreatePoint(tags: Tag[], layerToAddTo: FilteredLayer) {
 | 
				
			||||||
        return () => {
 | 
					        return () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const loc = State.state.bm.LastClickLocation.data;
 | 
					            const loc = State.state.LastClickLocation.data;
 | 
				
			||||||
            let feature = State.state.changes.createElement(tags, loc.lat, loc.lon);
 | 
					            let feature = State.state.changes.createElement(tags, loc.lat, loc.lon);
 | 
				
			||||||
            State.state.selectedElement.setData(feature);
 | 
					            State.state.selectedElement.setData(feature);
 | 
				
			||||||
            layerToAddTo.AddNewElement(feature);
 | 
					            layerToAddTo.AddNewElement(feature);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,7 +59,7 @@ export class UserBadge extends UIElement {
 | 
				
			||||||
            if (home === undefined) {
 | 
					            if (home === undefined) {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            State.state.bm.map.setView([home.lat, home.lon], 16);
 | 
					            State.state.leafletMap.data.setView([home.lat, home.lon], 16);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								index.ts
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								index.ts
									
										
									
									
									
								
							| 
						 | 
					@ -6,6 +6,7 @@ import {UIEventSource} from "./Logic/UIEventSource";
 | 
				
			||||||
import * as $ from "jquery";
 | 
					import * as $ from "jquery";
 | 
				
			||||||
import LayoutConfig from "./Customizations/JSON/LayoutConfig";
 | 
					import LayoutConfig from "./Customizations/JSON/LayoutConfig";
 | 
				
			||||||
import {Utils} from "./Utils";
 | 
					import {Utils} from "./Utils";
 | 
				
			||||||
 | 
					import {Overpass} from "./Logic/Osm/Overpass";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let defaultLayout = "bookcases"
 | 
					let defaultLayout = "bookcases"
 | 
				
			||||||
// --------------------- Special actions based on the parameters -----------------
 | 
					// --------------------- Special actions based on the parameters -----------------
 | 
				
			||||||
| 
						 | 
					@ -17,19 +18,9 @@ if (location.href.startsWith("http://buurtnatuur.be")) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (location.href.indexOf("buurtnatuur.be") >= 0) {
 | 
					if (location.href.indexOf("buurtnatuur.be") >= 0) {
 | 
				
			||||||
    // Reload the https version. This is important for the 'locate me' button
 | 
					 | 
				
			||||||
    defaultLayout = "buurtnatuur"
 | 
					    defaultLayout = "buurtnatuur"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
if (location.href.indexOf("buurtnatuur.be") >= 0) {
 | 
					 | 
				
			||||||
    defaultLayout = "buurtnatuur"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if(location.href.indexOf("pietervdvn.github.io") >= 0){
 | 
					 | 
				
			||||||
    defaultLayout = "bookcases"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const customCssQP = QueryParameters.GetQueryParameter("custom-css", "", "If specified, the custom css from the given link will be loaded additionaly");
 | 
					const customCssQP = QueryParameters.GetQueryParameter("custom-css", "", "If specified, the custom css from the given link will be loaded additionaly");
 | 
				
			||||||
if(customCssQP.data !== undefined && customCssQP.data !== ""){
 | 
					if(customCssQP.data !== undefined && customCssQP.data !== ""){
 | 
				
			||||||
    Utils.LoadCustomCss(customCssQP.data);
 | 
					    Utils.LoadCustomCss(customCssQP.data);
 | 
				
			||||||
| 
						 | 
					@ -43,7 +34,7 @@ if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
 | 
				
			||||||
    testing.setData(testing.data ?? "true")
 | 
					    testing.setData(testing.data ?? "true")
 | 
				
			||||||
    // If you have a testfile somewhere, enable this to spoof overpass
 | 
					    // If you have a testfile somewhere, enable this to spoof overpass
 | 
				
			||||||
    // This should be hosted independantly, e.g. with `cd assets; webfsd -p 8080` + a CORS plugin to disable cors rules
 | 
					    // This should be hosted independantly, e.g. with `cd assets; webfsd -p 8080` + a CORS plugin to disable cors rules
 | 
				
			||||||
    //Overpass.testUrl = "http://127.0.0.1:8080/streetwidths.geojson";
 | 
					    // Overpass.testUrl = "http://127.0.0.1:8080/streetwidths.geojson";
 | 
				
			||||||
} else {
 | 
					} else {
 | 
				
			||||||
    testing = QueryParameters.GetQueryParameter("test", "false");
 | 
					    testing = QueryParameters.GetQueryParameter("test", "false");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -78,7 +69,7 @@ if (layoutFromBase64.startsWith("wiki:")) {
 | 
				
			||||||
    $.ajax({
 | 
					    $.ajax({
 | 
				
			||||||
        url: url,
 | 
					        url: url,
 | 
				
			||||||
        success: function (data) {
 | 
					        success: function (data) {
 | 
				
			||||||
            // Hacky McHackFace has been working here. This probably break in the future
 | 
					            // Hacky McHackFace has been working here. This'll probably break in the future
 | 
				
			||||||
            const startTrigger = "<div class=\"mw-parser-output\">";
 | 
					            const startTrigger = "<div class=\"mw-parser-output\">";
 | 
				
			||||||
            const start = data.indexOf(startTrigger);
 | 
					            const start = data.indexOf(startTrigger);
 | 
				
			||||||
            data = data.substr(start, 
 | 
					            data = data.substr(start, 
 | 
				
			||||||
| 
						 | 
					@ -113,4 +104,3 @@ if (layoutFromBase64.startsWith("wiki:")) {
 | 
				
			||||||
window.addEventListener('contextmenu', function (e) { // Not compatible with IE < 9
 | 
					window.addEventListener('contextmenu', function (e) { // Not compatible with IE < 9
 | 
				
			||||||
    e.preventDefault();
 | 
					    e.preventDefault();
 | 
				
			||||||
}, false);
 | 
					}, false);
 | 
				
			||||||
// console.log(QueryParameters.GenerateQueryParameterDocs())
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue