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 {MoreScreen} from "./UI/MoreScreen"; | ||||
| import {FilteredLayer} from "./Logic/FilteredLayer"; | ||||
| import {Basemap} from "./Logic/Leaflet/Basemap"; | ||||
| import {Basemap} from "./UI/Basemap"; | ||||
| import State from "./State"; | ||||
| import {WelcomeMessage} from "./UI/WelcomeMessage"; | ||||
| import {LayerSelection} from "./UI/LayerSelection"; | ||||
|  | @ -17,7 +17,7 @@ import {UIEventSource} from "./Logic/UIEventSource"; | |||
| import {QueryParameters} from "./Logic/Web/QueryParameters"; | ||||
| import {PersonalLayersPanel} from "./UI/PersonalLayersPanel"; | ||||
| import Locale from "./UI/i18n/Locale"; | ||||
| import {StrayClickHandler} from "./Logic/Leaflet/StrayClickHandler"; | ||||
| import {StrayClickHandler} from "./Logic/Actors/StrayClickHandler"; | ||||
| import {SimpleAddUI} from "./UI/SimpleAddUI"; | ||||
| import {CenterMessageBox} from "./UI/CenterMessageBox"; | ||||
| import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; | ||||
|  | @ -25,11 +25,10 @@ import {TagUtils} from "./Logic/Tags"; | |||
| import {UserBadge} from "./UI/UserBadge"; | ||||
| import {SearchAndGo} from "./UI/SearchAndGo"; | ||||
| import {FullScreenMessageBox} from "./UI/FullScreenMessageBoxHandler"; | ||||
| import {GeoLocationHandler} from "./Logic/Leaflet/GeoLocationHandler"; | ||||
| import {GeoLocationHandler} from "./Logic/Actors/GeoLocationHandler"; | ||||
| import {LocalStorageSource} from "./Logic/Web/LocalStorageSource"; | ||||
| import {Utils} from "./Utils"; | ||||
| import BackgroundSelector from "./UI/BackgroundSelector"; | ||||
| import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; | ||||
| import {FeatureInfoBox} from "./UI/Popup/FeatureInfoBox"; | ||||
| import Svg from "./Svg"; | ||||
| import Link from "./UI/Base/Link"; | ||||
|  | @ -44,40 +43,6 @@ import Constants from "./Models/Constants"; | |||
| 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, | ||||
|                    layoutDefinition: string = "") { | ||||
|         if (layoutToUse === undefined) { | ||||
|  | @ -88,6 +53,8 @@ export class InitUiElements { | |||
|         } | ||||
| 
 | ||||
|         console.log("Using layout: ", layoutToUse.id, "LayoutFromBase64 is ", layoutFromBase64); | ||||
| 
 | ||||
| 
 | ||||
|         State.state = new State(layoutToUse); | ||||
| 
 | ||||
|         // This 'leaks' the global state via the window object, useful for debugging
 | ||||
|  | @ -231,10 +198,14 @@ export class InitUiElements { | |||
|                     iconAnchor: [15, 15] | ||||
|                 }); | ||||
|                 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;`) | ||||
|             .AttachTo("geolocate-button"); | ||||
|         State.state.locationControl.ping(); | ||||
|  | @ -282,6 +253,181 @@ export class InitUiElements { | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     static InitWelcomeMessage() { | ||||
| 
 | ||||
|         const fullOptions = this.CreateWelcomePane(); | ||||
| 
 | ||||
|         const help = Svg.help_svg().SetClass("open-welcome-button"); | ||||
|         const close = Svg.close_svg().SetClass("close-welcome-button"); | ||||
|         const checkbox = new CheckBox( | ||||
|             new Combine([ | ||||
|                 close, | ||||
|                 fullOptions | ||||
|                     .SetClass("welcomeMessage") | ||||
|                     .onClick(() => {/*Catch the click*/ | ||||
|                     })]), | ||||
|             help | ||||
|             , true | ||||
|         ).AttachTo("messagesbox"); | ||||
|         const openedTime = new Date().getTime(); | ||||
|         State.state.locationControl.addCallback(() => { | ||||
|             if (new Date().getTime() - openedTime < 15 * 1000) { | ||||
|                 // Don't autoclose the first 15 secs when the map is moving
 | ||||
|                 return; | ||||
|             } | ||||
|             checkbox.isEnabled.setData(false); | ||||
|         }) | ||||
| 
 | ||||
|         State.state.selectedElement.addCallback(() => { | ||||
|             checkbox.isEnabled.setData(false); | ||||
|         }) | ||||
| 
 | ||||
| 
 | ||||
|         const fullOptions2 = this.CreateWelcomePane(); | ||||
|         State.state.fullScreenMessage.setData(fullOptions2) | ||||
| 
 | ||||
|         Svg.help_svg() | ||||
|             .SetClass("open-welcome-button") | ||||
|             .SetClass("shadow") | ||||
|             .onClick(() => { | ||||
|                 State.state.fullScreenMessage.setData(fullOptions2) | ||||
|             }).AttachTo("help-button-mobile"); | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     static InitLayerSelection() { | ||||
|         InitUiElements.OnlyIf(State.state.featureSwitchLayers, () => { | ||||
| 
 | ||||
|             const layerControlPanel = this.GenerateLayerControlPanel(); | ||||
|             if (layerControlPanel === undefined) { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             layerControlPanel.SetStyle("display:block;padding:0.75em;border-radius:1em;"); | ||||
|             const closeButton = Svg.close_svg().SetClass("layer-selection-toggle").SetStyle("  background: var(--subtle-detail-color);") | ||||
|             const checkbox = new CheckBox( | ||||
|                 new Combine([ | ||||
|                     closeButton, | ||||
|                     layerControlPanel]).SetStyle("display:flex;flex-direction:row;") | ||||
|                     .SetClass("hidden-on-mobile") | ||||
|                 , | ||||
|                 Svg.layers_svg().SetClass("layer-selection-toggle"), | ||||
|                 State.state.layerControlIsOpened | ||||
|             ); | ||||
| 
 | ||||
|             checkbox.AttachTo("layer-selection"); | ||||
| 
 | ||||
| 
 | ||||
|             State.state.locationControl.addCallback(() => { | ||||
|                 // Close the layer selection when the map is moved
 | ||||
|                 checkbox.isEnabled.setData(false); | ||||
|             }); | ||||
| 
 | ||||
|             const fullScreen = this.GenerateLayerControlPanel(); | ||||
|             checkbox.isEnabled.addCallback(isEnabled => { | ||||
|                 if (isEnabled) { | ||||
|                     State.state.fullScreenMessage.setData(fullScreen); | ||||
|                 } | ||||
|             }) | ||||
|             State.state.fullScreenMessage.addCallbackAndRun(latest => { | ||||
|                 if (latest === undefined) { | ||||
|                     checkbox.isEnabled.setData(false); | ||||
|                 } | ||||
|             }) | ||||
| 
 | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     static InitBaseMap() { | ||||
|          | ||||
|         const attr = new Attribution(State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, State.state.leafletMap); | ||||
|         const bm = new Basemap("leafletDiv", | ||||
|             State.state.locationControl, | ||||
|             State.state.backgroundLayer, | ||||
|             State.state.LastClickLocation, | ||||
|             attr | ||||
|         ); | ||||
|         State.state.leafletMap.setData(bm.map); | ||||
|          | ||||
|         bm.map.on("popupclose", () => { | ||||
|             State.state.selectedElement.setData(undefined) | ||||
|         }) | ||||
|          | ||||
|          | ||||
|         State.state.layerUpdater = new UpdateFromOverpass(State.state); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     static InitLayers() { | ||||
| 
 | ||||
|         const flayers: FilteredLayer[] = [] | ||||
| 
 | ||||
|         const state = State.state; | ||||
| 
 | ||||
|         for (const layer of state.layoutToUse.data.layers) { | ||||
| 
 | ||||
|             if (typeof (layer) === "string") { | ||||
|                 throw "Layer " + layer + " was not substituted"; | ||||
|             } | ||||
| 
 | ||||
|             let generateContents = (tags: UIEventSource<any>) => new FeatureInfoBox(tags, layer); | ||||
|             if (layer.title === undefined && (layer.tagRenderings ?? []).length === 0) { | ||||
|                 generateContents = undefined; | ||||
|             } | ||||
| 
 | ||||
|             const flayer: FilteredLayer = new FilteredLayer(layer, generateContents); | ||||
|             flayers.push(flayer); | ||||
| 
 | ||||
|             QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wehter or not layer " + layer.id + " is shown") | ||||
|                 .map<boolean>((str) => str !== "false", [], (b) => b.toString()) | ||||
|                 .syncWith( | ||||
|                     flayer.isDisplayed | ||||
|                 ) | ||||
|         } | ||||
| 
 | ||||
|         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() { | ||||
| 
 | ||||
|  | @ -330,50 +476,6 @@ export class InitUiElements { | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     static InitWelcomeMessage() { | ||||
| 
 | ||||
|         const fullOptions = this.CreateWelcomePane(); | ||||
| 
 | ||||
|         const help = Svg.help_svg().SetClass("open-welcome-button"); | ||||
|         const close = Svg.close_svg().SetClass("close-welcome-button"); | ||||
|         const checkbox = new CheckBox( | ||||
|             new Combine([ | ||||
|                 close, | ||||
|                 fullOptions | ||||
|                     .SetClass("welcomeMessage") | ||||
|                     .onClick(() => {/*Catch the click*/ | ||||
|                     })]), | ||||
|             help | ||||
|             , true | ||||
|         ).AttachTo("messagesbox"); | ||||
|         const openedTime = new Date().getTime(); | ||||
|         State.state.locationControl.addCallback(() => { | ||||
|             if (new Date().getTime() - openedTime < 15 * 1000) { | ||||
|                 // Don't autoclose the first 15 secs when the map is moving
 | ||||
|                 return; | ||||
|             } | ||||
|             checkbox.isEnabled.setData(false); | ||||
|         }) | ||||
| 
 | ||||
|         State.state.selectedElement.addCallback(() => { | ||||
|             checkbox.isEnabled.setData(false); | ||||
|         }) | ||||
| 
 | ||||
| 
 | ||||
|         const fullOptions2 = this.CreateWelcomePane(); | ||||
|         State.state.fullScreenMessage.setData(fullOptions2) | ||||
| 
 | ||||
|         Svg.help_svg() | ||||
|             .SetClass("open-welcome-button") | ||||
|             .SetClass("shadow") | ||||
|             .onClick(() => { | ||||
|                 State.state.fullScreenMessage.setData(fullOptions2) | ||||
|             }).AttachTo("help-button-mobile"); | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
|      | ||||
|     private static GenerateLayerControlPanel() { | ||||
| 
 | ||||
| 
 | ||||
|  | @ -381,7 +483,8 @@ export class InitUiElements { | |||
|         if (State.state.layoutToUse.data.enableBackgroundLayerSelection) { | ||||
|             layerControlPanel = new BackgroundSelector(); | ||||
|             layerControlPanel.SetStyle("margin:1em"); | ||||
|             layerControlPanel.onClick(() => {            }); | ||||
|             layerControlPanel.onClick(() => { | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         if (State.state.filteredLayers.data.length > 1) { | ||||
|  | @ -393,107 +496,4 @@ export class InitUiElements { | |||
|         return layerControlPanel; | ||||
|     } | ||||
| 
 | ||||
|     static InitLayerSelection() { | ||||
|         InitUiElements.OnlyIf(State.state.featureSwitchLayers, () => { | ||||
| 
 | ||||
|             const layerControlPanel = this.GenerateLayerControlPanel(); | ||||
|             if (layerControlPanel === undefined) { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             layerControlPanel.SetStyle("display:block;padding:0.75em;border-radius:1em;"); | ||||
|             const closeButton = Svg.close_svg().SetClass("layer-selection-toggle").SetStyle("  background: var(--subtle-detail-color);") | ||||
|             const checkbox = new CheckBox( | ||||
|                 new Combine([ | ||||
|                     closeButton, | ||||
|                     layerControlPanel]).SetStyle("display:flex;flex-direction:row;") | ||||
|                     .SetClass("hidden-on-mobile") | ||||
|                 , | ||||
|                 Svg.layers_svg().SetClass("layer-selection-toggle"), | ||||
|                 State.state.layerControlIsOpened | ||||
|             ); | ||||
| 
 | ||||
|             checkbox.AttachTo("layer-selection"); | ||||
| 
 | ||||
| 
 | ||||
|             State.state.locationControl.addCallback(() => { | ||||
|                 // Close the layer selection when the map is moved
 | ||||
|                 checkbox.isEnabled.setData(false); | ||||
|             }); | ||||
| 
 | ||||
|             const fullScreen = this.GenerateLayerControlPanel(); | ||||
|             checkbox.isEnabled.addCallback(isEnabled => { | ||||
|                 if (isEnabled) { | ||||
|                     State.state.fullScreenMessage.setData(fullScreen); | ||||
|                 } | ||||
|             }) | ||||
|             State.state.fullScreenMessage.addCallbackAndRun(latest => { | ||||
|                 if (latest === undefined) { | ||||
|                     checkbox.isEnabled.setData(false); | ||||
|                 } | ||||
|             }) | ||||
| 
 | ||||
|         }); | ||||
|     } | ||||
|     static InitBaseMap() { | ||||
|         const bm = new Basemap("leafletDiv",  | ||||
|             State.state.locationControl,  | ||||
|             State.state.backgroundLayer, | ||||
|             new Attribution(State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, State.state.bm) | ||||
|         ); | ||||
|         State.state.bm = bm; | ||||
|         bm.map.on("popupclose", () => { | ||||
|             State.state.selectedElement.setData(undefined) | ||||
|         }) | ||||
|         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() { | ||||
| 
 | ||||
|         const flayers: FilteredLayer[] = [] | ||||
| 
 | ||||
|         const state = State.state; | ||||
| 
 | ||||
|         for (const layer of state.layoutToUse.data.layers) { | ||||
| 
 | ||||
|             if (typeof (layer) === "string") { | ||||
|                 throw "Layer " + layer + " was not substituted"; | ||||
|             } | ||||
| 
 | ||||
|             let generateContents = (tags: UIEventSource<any>) => new FeatureInfoBox(tags, layer); | ||||
|             if (layer.title === undefined && (layer.tagRenderings ?? []).length === 0) { | ||||
|                 generateContents = undefined; | ||||
|             } | ||||
| 
 | ||||
|             const flayer: FilteredLayer = new FilteredLayer(layer, generateContents); | ||||
|             flayers.push(flayer); | ||||
| 
 | ||||
|             QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wehter or not layer " + layer.id + " is shown") | ||||
|                 .map<boolean>((str) => str !== "false", [], (b) => b.toString()) | ||||
|                 .syncWith( | ||||
|                     flayer.isDisplayed | ||||
|                 ) | ||||
|         } | ||||
| 
 | ||||
|         State.state.filteredLayers.setData(flayers); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -1,10 +1,9 @@ | |||
| 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 {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 {Utils} from "../../Utils"; | ||||
| 
 | ||||
|  | @ -32,12 +31,16 @@ export default class AvailableBaseLayers { | |||
|     public static layerOverview = AvailableBaseLayers.LoadRasterIndex().concat(AvailableBaseLayers.LoadProviderIndex()); | ||||
|     public availableEditorLayers: UIEventSource<BaseLayer[]>; | ||||
| 
 | ||||
|     constructor(location: UIEventSource<{ lat: number, lon: number, zoom: number }>, | ||||
|                 currentBackgroundLayer: UIEventSource<BaseLayer>) { | ||||
|     constructor(location: UIEventSource<{ lat: number, lon: number, zoom: number }>) { | ||||
|         const self = this; | ||||
|         this.availableEditorLayers = | ||||
|             location.map( | ||||
|                 (currentLocation) => { | ||||
|                      | ||||
|                     if(currentLocation === undefined){ | ||||
|                         return AvailableBaseLayers.layerOverview; | ||||
|                     } | ||||
|                      | ||||
|                     const currentLayers = self.availableEditorLayers?.data; | ||||
|                     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[] { | ||||
|         const availableLayers = [AvailableBaseLayers.osmCarto] | ||||
|         const globalLayers = []; | ||||
|         for (const i in AvailableBaseLayers.layerOverview) { | ||||
|             const layer = AvailableBaseLayers.layerOverview[i]; | ||||
|         for (const layerOverviewItem of AvailableBaseLayers.layerOverview) { | ||||
|             const layer = layerOverviewItem; | ||||
|              | ||||
|             if (layer.feature?.geometry === undefined || layer.feature?.geometry === null) { | ||||
|                 globalLayers.push(layer); | ||||
|                 continue; | ||||
|  |  | |||
|  | @ -1,9 +1,7 @@ | |||
| import * as L from "leaflet"; | ||||
| import {UIEventSource} from "../UIEventSource"; | ||||
| import {UIElement} from "../../UI/UIElement"; | ||||
| import State from "../../State"; | ||||
| import {Utils} from "../../Utils"; | ||||
| import {Basemap} from "./Basemap"; | ||||
| import Svg from "../../Svg"; | ||||
| 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 _permission: UIEventSource<string> = new UIEventSource<string>(""); | ||||
|     private _marker: any; | ||||
|     private _marker: L.Marker; | ||||
|     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); | ||||
|         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; | ||||
|         import("../../vendor/Leaflet.AccuratePosition.js").then(() => { | ||||
|             self.init(); | ||||
|  | @ -33,11 +39,11 @@ export class GeoLocationHandler extends UIElement { | |||
| 
 | ||||
| 
 | ||||
|         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) { | ||||
|             State.state.currentGPSLocation.setData({latlng: e.latlng, accuracy: e.accuracy}); | ||||
|             self._currentGPSLocation.setData({latlng: e.latlng, accuracy: e.accuracy}); | ||||
|         } | ||||
| 
 | ||||
|         function onAccuratePositionError(e) { | ||||
|  | @ -45,15 +51,13 @@ export class GeoLocationHandler extends UIElement { | |||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         const bm : Basemap = State.state.bm; | ||||
|         const map = bm.map; | ||||
|         const map = this._leafletMap.data; | ||||
|         map.on('accuratepositionprogress', onAccuratePositionProgress); | ||||
|         map.on('accuratepositionfound', onAccuratePositionFound); | ||||
|         map.on('accuratepositionerror', onAccuratePositionError); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         State.state.currentGPSLocation.addCallback((location) => { | ||||
|         this._currentGPSLocation.addCallback((location) => { | ||||
| 
 | ||||
|             const color = getComputedStyle(document.body).getPropertyValue("--catch-detail-color") | ||||
|             const icon = L.icon( | ||||
|  | @ -88,7 +92,7 @@ export class GeoLocationHandler extends UIElement { | |||
|     } | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
|         if(!State.state.featureSwitchGeolocation.data){ | ||||
|         if (!this._featureSwitch.data) { | ||||
|             return ""; | ||||
|         } | ||||
| 
 | ||||
|  | @ -102,16 +106,31 @@ export class GeoLocationHandler extends UIElement { | |||
|         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) { | ||||
|         const self = this; | ||||
|         const map = State.state.bm.map; | ||||
|         const map : any = this._leafletMap.data; | ||||
|         if (self._permission.data === "denied") { | ||||
|             return ""; | ||||
|         } | ||||
|         if (State.state.currentGPSLocation.data !== undefined) { | ||||
|             State.state.bm.map.setView( | ||||
|                 State.state.currentGPSLocation.data.latlng, zoomlevel | ||||
|         if (this._currentGPSLocation.data !== undefined) { | ||||
|             this._leafletMap.data.setView( | ||||
|                 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 {UIElement} from "../../UI/UIElement"; | ||||
| import State from "../../State"; | ||||
| import {Img} from "../../UI/Img"; | ||||
| 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. | ||||
|  | @ -13,25 +14,29 @@ export class StrayClickHandler { | |||
|     private _uiToShow: (() => UIElement); | ||||
| 
 | ||||
|     constructor( | ||||
|         lastClickLocation: UIEventSource<{ lat: number, lon:number }>, | ||||
|         selectedElement: UIEventSource<string>, | ||||
|         filteredLayers: UIEventSource<FilteredLayer[]>, | ||||
|         leafletMap: UIEventSource<L.Map>, | ||||
|         fullscreenMessage: UIEventSource<UIElement>, | ||||
|         uiToShow: (() => UIElement)) { | ||||
|         this._uiToShow = uiToShow; | ||||
|         const self = this; | ||||
|         const map = State.state.bm.map; | ||||
|         State.state.filteredLayers.data.forEach((filteredLayer) => { | ||||
|         filteredLayers.data.forEach((filteredLayer) => { | ||||
|             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
 | ||||
|                     // 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) { | ||||
|             State.state.selectedElement.setData(undefined); | ||||
|         lastClickLocation.addCallback(function (lastClick) { | ||||
|             selectedElement.setData(undefined); | ||||
| 
 | ||||
|             if (self._lastMarker !== undefined) { | ||||
|                 map.removeLayer(self._lastMarker); | ||||
|                 leafletMap.data?.removeLayer(self._lastMarker); | ||||
|             } | ||||
|             self._lastMarker = L.marker([lastClick.lat, lastClick.lon], { | ||||
|                 icon: L.icon({ | ||||
|  | @ -43,18 +48,18 @@ export class StrayClickHandler { | |||
|             }); | ||||
|             const uiElement = uiToShow(); | ||||
|             const popup = L.popup().setContent(uiElement.Render()); | ||||
|             self._lastMarker.addTo(map); | ||||
|             self._lastMarker.addTo(leafletMap.data); | ||||
|             self._lastMarker.bindPopup(popup); | ||||
| 
 | ||||
|             self._lastMarker.on("click", () => { | ||||
|                 State.state.fullScreenMessage.setData(self._uiToShow()); | ||||
|                 fullscreenMessage.setData(self._uiToShow()); | ||||
|                 uiElement.Update(); | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         State.state.selectedElement.addCallback(() => { | ||||
|         selectedElement.addCallback(() => { | ||||
|             if (self._lastMarker !== undefined) { | ||||
|                 map.removeLayer(self._lastMarker); | ||||
|                 leafletMap.data.removeLayer(self._lastMarker); | ||||
|                 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"; | ||||
| 
 | ||||
|  |  | |||
|  | @ -58,7 +58,7 @@ export class FilteredLayer { | |||
|             [State.state.locationControl] | ||||
|         ); | ||||
|         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 (isDisplayed) { | ||||
|                     self._geolayer.addTo(map); | ||||
|  | @ -116,7 +116,7 @@ export class FilteredLayer { | |||
| 
 | ||||
|         if (this._geolayer !== undefined && this._geolayer !== null) { | ||||
|             // 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:
 | ||||
|  | @ -200,7 +200,7 @@ export class FilteredLayer { | |||
|                 | ||||
|                     const center = GeoOperations.centerpoint(feature).geometry.coordinates; | ||||
|                     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); | ||||
|                     uiElement.Update(); | ||||
|                 } | ||||
|  | @ -209,7 +209,7 @@ export class FilteredLayer { | |||
|         }); | ||||
| 
 | ||||
|         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, | ||||
|                   handleResult: ((places: { display_name: string, lat: number, lon: number, boundingbox: number[] }[]) => void), | ||||
|                   onFail: (() => void)) { | ||||
|         const b = State.state.bm.map.getBounds(); | ||||
|         const b = State.state.leafletMap.data.getBounds(); | ||||
|         console.log(b); | ||||
|         $.getJSON( | ||||
|             Geocoding.host + "format=json&limit=1&viewbox=" +  | ||||
|  |  | |||
|  | @ -162,7 +162,7 @@ export class UpdateFromOverpass { | |||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const bounds = state.bm.map.getBounds(); | ||||
|         const bounds = state.leafletMap.data.getBounds(); | ||||
| 
 | ||||
|         const diff = state.layoutToUse.data.widenFactor; | ||||
| 
 | ||||
|  | @ -196,7 +196,7 @@ export class UpdateFromOverpass { | |||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         const b = state.bm.map.getBounds(); | ||||
|         const b = state.leafletMap.data.getBounds(); | ||||
|         return b.getSouth() >= bounds.south && | ||||
|             b.getNorth() <= bounds.north && | ||||
|             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 Constants from "./Models/Constants"; | ||||
| 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 | ||||
|  | @ -41,9 +43,9 @@ export default class State { | |||
|      */ | ||||
|     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 | ||||
|      */ | ||||
|  | @ -91,7 +93,10 @@ export default class State { | |||
|      * The map location: currently centered lat, lon and zoom | ||||
|      */ | ||||
|     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 | ||||
|  | @ -114,23 +119,12 @@ export default class State { | |||
|         const self = this; | ||||
|         this.layoutToUse.setData(layoutToUse); | ||||
| 
 | ||||
|         function 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); | ||||
|             }) | ||||
|         } | ||||
|         const zoom = asFloat( | ||||
|         const zoom = State.asFloat( | ||||
|             QueryParameters.GetQueryParameter("z", "" + layoutToUse.startZoom, "The initial/current zoom level") | ||||
|             .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"))); | ||||
|         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"))); | ||||
| 
 | ||||
| 
 | ||||
|  | @ -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> { | ||||
|             const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined, documentation); | ||||
|             // 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( | ||||
|                 self.layoutToUse.map((layout) => { | ||||
|                     const defaultValue = deflt(layout); | ||||
|  | @ -186,6 +204,9 @@ export default class State { | |||
|         this.featureSwitchGeolocation = featSw("fs-geolocation", (layoutToUse) => layoutToUse?.enableGeolocation ?? true, | ||||
|             "Disables/Enables the geolocation button"); | ||||
| 
 | ||||
| 
 | ||||
|          | ||||
|          | ||||
|         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; | ||||
|         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 {UIEventSource} from "../UIEventSource"; | ||||
| import {UIElement} from "../../UI/UIElement"; | ||||
| import {BaseLayer} from "../../Models/BaseLayer"; | ||||
| import AvailableBaseLayers from "../Actors/AvailableBaseLayers"; | ||||
| import Loc from "../../Models/Loc"; | ||||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import Loc from "../Models/Loc"; | ||||
| import {UIElement} from "./UIElement"; | ||||
| import {BaseLayer} from "../Models/BaseLayer"; | ||||
| 
 | ||||
| export class Basemap { | ||||
| 
 | ||||
| 
 | ||||
|     // @ts-ignore
 | ||||
|     public readonly map: Map; | ||||
| 
 | ||||
|     public readonly LastClickLocation: UIEventSource<{ lat: number, lon: number }> = new UIEventSource<{ lat: number, lon: number }>(undefined) | ||||
| 
 | ||||
|     public readonly map: L.Map; | ||||
| 
 | ||||
|     constructor(leafletElementId: string, | ||||
|                 location: UIEventSource<Loc>, | ||||
|                 currentLayer: UIEventSource<BaseLayer>, | ||||
|                 lastClickLocation: UIEventSource<{ lat: number, lon: number }>, | ||||
|                 extraAttribution: UIElement) { | ||||
|         this.map = L.map(leafletElementId, { | ||||
|             center: [location.data.lat ?? 0, location.data.lon ?? 0], | ||||
|             zoom: location.data.zoom ?? 2, | ||||
|             layers: [AvailableBaseLayers.osmCarto.layer], | ||||
|             layers: [currentLayer.data.layer], | ||||
|         }); | ||||
| 
 | ||||
|         L.control.scale( | ||||
|  | @ -64,11 +60,14 @@ export class Basemap { | |||
|         }); | ||||
| 
 | ||||
|         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) { | ||||
|             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(); | ||||
|         }); | ||||
| 
 | ||||
|  | @ -2,10 +2,7 @@ import {InputElement} from "./InputElement"; | |||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import Combine from "../Base/Combine"; | ||||
| 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 | ||||
|  |  | |||
|  | @ -1,30 +1,30 @@ | |||
| import {UIElement} from "../UIElement"; | ||||
| import Link from "../Base/Link"; | ||||
| import Svg from "../../Svg"; | ||||
| import {Basemap} from "../../Logic/Leaflet/Basemap"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {UserDetails} from "../../Logic/Osm/OsmConnection"; | ||||
| import Constants from "../../Models/Constants"; | ||||
| import LayoutConfig from "../../Customizations/JSON/LayoutConfig"; | ||||
| import Loc from "../../Models/Loc"; | ||||
| import * as L from "leaflet" | ||||
| 
 | ||||
| export default class Attribution extends UIElement { | ||||
|      | ||||
|     private readonly _location: UIEventSource<Loc>; | ||||
|     private readonly _layoutToUse: UIEventSource<LayoutConfig>; | ||||
|     private readonly _userDetails: UIEventSource<UserDetails>; | ||||
|     private readonly _basemap: Basemap; | ||||
|     private readonly _leafletMap: UIEventSource<L.Map>; | ||||
| 
 | ||||
|     constructor(location: UIEventSource<Loc>, | ||||
|                 userDetails: UIEventSource<UserDetails>, | ||||
|                 layoutToUse: UIEventSource<LayoutConfig>, | ||||
|                 basemap: Basemap) { | ||||
|                 leafletMap: UIEventSource<L.Map>) { | ||||
|         super(location); | ||||
|         this._layoutToUse = layoutToUse; | ||||
|         this.ListenTo(layoutToUse); | ||||
|         this._userDetails = userDetails; | ||||
|         this._basemap = basemap; | ||||
|         this._leafletMap = leafletMap; | ||||
|         this.ListenTo(userDetails); | ||||
|         this._location = location; | ||||
|         this.SetClass("map-attribution"); | ||||
|  | @ -47,9 +47,9 @@ export default class Attribution extends UIElement { | |||
|         } | ||||
|         let editWithJosm: (UIElement | string) = "" | ||||
|         if (location !== undefined && | ||||
|             this._basemap !== undefined && | ||||
|             this._leafletMap.data !== undefined && | ||||
|             userDetails.csCount >=  Constants.userJourney.tagsVisibleAndWikiLinked) { | ||||
|             const bounds = this._basemap.map.getBounds(); | ||||
|             const bounds = this._leafletMap.data.getBounds(); | ||||
|             const top = bounds.getNorth(); | ||||
|             const bottom = bounds.getSouth(); | ||||
|             const right = bounds.getEast(); | ||||
|  |  | |||
|  | @ -61,7 +61,7 @@ export class SearchAndGo extends UIElement { | |||
|                     [bb[0], bb[2]], | ||||
|                     [bb[1], bb[3]] | ||||
|                 ] | ||||
|                 State.state.bm.map.fitBounds(bounds); | ||||
|                 State.state.leafletMap.data.fitBounds(bounds); | ||||
|                 self._placeholder.setData(Translations.t.general.search.search); | ||||
|             }, | ||||
|             () => { | ||||
|  |  | |||
|  | @ -115,7 +115,7 @@ export class SimpleAddUI extends UIElement { | |||
|     private CreatePoint(tags: Tag[], layerToAddTo: FilteredLayer) { | ||||
|         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); | ||||
|             State.state.selectedElement.setData(feature); | ||||
|             layerToAddTo.AddNewElement(feature); | ||||
|  |  | |||
|  | @ -59,7 +59,7 @@ export class UserBadge extends UIElement { | |||
|             if (home === undefined) { | ||||
|                 return; | ||||
|             } | ||||
|             State.state.bm.map.setView([home.lat, home.lon], 16); | ||||
|             State.state.leafletMap.data.setView([home.lat, home.lon], 16); | ||||
|         }); | ||||
| 
 | ||||
|     } | ||||
|  |  | |||
							
								
								
									
										14
									
								
								index.ts
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								index.ts
									
										
									
									
									
								
							|  | @ -6,6 +6,7 @@ import {UIEventSource} from "./Logic/UIEventSource"; | |||
| import * as $ from "jquery"; | ||||
| import LayoutConfig from "./Customizations/JSON/LayoutConfig"; | ||||
| import {Utils} from "./Utils"; | ||||
| import {Overpass} from "./Logic/Osm/Overpass"; | ||||
| 
 | ||||
| let defaultLayout = "bookcases" | ||||
| // --------------------- Special actions based on the parameters -----------------
 | ||||
|  | @ -17,19 +18,9 @@ if (location.href.startsWith("http://buurtnatuur.be")) { | |||
| 
 | ||||
| 
 | ||||
| if (location.href.indexOf("buurtnatuur.be") >= 0) { | ||||
|     // Reload the https version. This is important for the 'locate me' button
 | ||||
|     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"); | ||||
| if(customCssQP.data !== undefined && customCssQP.data !== ""){ | ||||
|     Utils.LoadCustomCss(customCssQP.data); | ||||
|  | @ -78,7 +69,7 @@ if (layoutFromBase64.startsWith("wiki:")) { | |||
|     $.ajax({ | ||||
|         url: url, | ||||
|         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 start = data.indexOf(startTrigger); | ||||
|             data = data.substr(start,  | ||||
|  | @ -113,4 +104,3 @@ if (layoutFromBase64.startsWith("wiki:")) { | |||
| window.addEventListener('contextmenu', function (e) { // Not compatible with IE < 9
 | ||||
|     e.preventDefault(); | ||||
| }, false); | ||||
| // console.log(QueryParameters.GenerateQueryParameterDocs())
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue