forked from MapComplete/MapComplete
		
	Add a personal, configurable quest
This commit is contained in:
		
							parent
							
								
									9c42839f01
								
							
						
					
					
						commit
						7ec00a3301
					
				
					 21 changed files with 376 additions and 237 deletions
				
			
		|  | @ -12,13 +12,14 @@ import {Natuurpunt} from "./Layouts/Natuurpunt"; | |||
| import {ClimbingTrees} from "./Layouts/ClimbingTrees"; | ||||
| import {Smoothness} from "./Layouts/Smoothness"; | ||||
| import {LayerDefinition} from "./LayerDefinition"; | ||||
| import {CustomLayers} from "../Logic/CustomLayers"; | ||||
| import {CustomLayout} from "../Logic/CustomLayers"; | ||||
| 
 | ||||
| export class AllKnownLayouts { | ||||
| 
 | ||||
|     public static allLayers: Map<string, LayerDefinition> = undefined; | ||||
|      | ||||
|     public static layoutsList: Layout[] = [ | ||||
|         new CustomLayout(), | ||||
|         new Groen(), | ||||
|         new GRB(), | ||||
|         new Cyclofix(), | ||||
|  | @ -30,7 +31,6 @@ export class AllKnownLayouts { | |||
|         new ClimbingTrees(), | ||||
|         new Artworks(), | ||||
|         new Smoothness(), | ||||
|         new CustomLayers() | ||||
|         /*new Toilets(), | ||||
|         */ | ||||
|     ]; | ||||
|  |  | |||
|  | @ -3,6 +3,13 @@ import {UIElement} from "../UI/UIElement"; | |||
| import {TagRenderingOptions} from "./TagRendering"; | ||||
| import {TagDependantUIElementConstructor} from "./UIElementConstructor"; | ||||
| 
 | ||||
| export interface Preset { | ||||
|     tags: Tag[], | ||||
|     title: string | UIElement, | ||||
|     description?: string | UIElement, | ||||
|     icon?: string | ||||
| } | ||||
| 
 | ||||
| export class LayerDefinition { | ||||
| 
 | ||||
| 
 | ||||
|  | @ -20,12 +27,7 @@ export class LayerDefinition { | |||
|      * These tags are added whenever a new point is added by the user on the map. | ||||
|      * This is the ideal place to add extra info, such as "fixme=added by MapComplete, geometry should be checked" | ||||
|      */ | ||||
|     presets: { | ||||
|         tags: Tag[], | ||||
|         title: string | UIElement, | ||||
|         description?: string | UIElement, | ||||
|         icon?: string | ||||
|     }[] | ||||
|     presets: Preset[] | ||||
|     /** | ||||
|      * Not really used anymore | ||||
|      * This is meant to serve as icon in the buttons | ||||
|  |  | |||
|  | @ -13,7 +13,6 @@ import {Tag} from "./Logic/TagsFilter"; | |||
| import {FilteredLayer} from "./Logic/FilteredLayer"; | ||||
| import {FeatureInfoBox} from "./UI/FeatureInfoBox"; | ||||
| import {ElementStorage} from "./Logic/ElementStorage"; | ||||
| import {Preset} from "./UI/SimpleAddUI"; | ||||
| import {Changes} from "./Logic/Osm/Changes"; | ||||
| import {OsmConnection} from "./Logic/Osm/OsmConnection"; | ||||
| import {BaseLayers, Basemap} from "./Logic/Leaflet/Basemap"; | ||||
|  | @ -23,6 +22,8 @@ import {Img} from "./UI/Img"; | |||
| import {DropDown} from "./UI/Input/DropDown"; | ||||
| import {LayerSelection} from "./UI/LayerSelection"; | ||||
| import {CustomLayersPanel} from "./Logic/CustomLayersPanel"; | ||||
| import {CustomLayout} from "./Logic/CustomLayers"; | ||||
| import {Preset} from "./Customizations/LayerDefinition"; | ||||
| 
 | ||||
| export class InitUiElements { | ||||
| 
 | ||||
|  | @ -44,15 +45,18 @@ export class InitUiElements { | |||
| 
 | ||||
|     private static CreateWelcomePane() { | ||||
| 
 | ||||
|         const welcome = new WelcomeMessage() | ||||
| 
 | ||||
|         const layoutToUse = State.state.layoutToUse.data; | ||||
|         let welcome: UIElement = new WelcomeMessage(); | ||||
|         if (layoutToUse.name === CustomLayout.NAME) { | ||||
|             welcome = new CustomLayersPanel(); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         const fullOptions = new TabbedComponent([ | ||||
|             {header: `<img src='${layoutToUse.icon}'>`, content: welcome}, | ||||
|             {header: `<img src='${'./assets/osm-logo.svg'}'>`, content: Translations.t.general.openStreetMapIntro}, | ||||
|             {header: `<img src='${'./assets/share.svg'}'>`, content: new ShareScreen()}, | ||||
|             {header: `<img src='${'./assets/add.svg'}'>`, content: new MoreScreen()}, | ||||
|             {header: `<img src='${'./assets/star.svg'}'>`, content: new CustomLayersPanel()}, | ||||
|         ]) | ||||
| 
 | ||||
|         return fullOptions; | ||||
|  | @ -94,7 +98,7 @@ export class InitUiElements { | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     static InitLayerSelection(layerSetup) { | ||||
|     static InitLayerSelection() { | ||||
|         const closedFilterButton = `<button id="filter__button" class="filter__button shadow">${Img.closedFilterButton}</button>`; | ||||
| 
 | ||||
|         const openFilterButton = `<button id="filter__button" class="filter__button">${Img.openFilterButton}</button>`; | ||||
|  | @ -103,9 +107,9 @@ export class InitUiElements { | |||
|             return {value: layer, shown: layer.name} | ||||
|         }); | ||||
|         const backgroundMapPicker = new Combine([new DropDown(`Background map`, baseLayerOptions, State.state.bm.CurrentLayer), openFilterButton]); | ||||
|         const layerSelection = new Combine([`<p class="filter__label">Maplayers</p>`, new LayerSelection(layerSetup.flayers)]); | ||||
|         const layerSelection = new Combine([`<p class="filter__label">Maplayers</p>`, new LayerSelection()]); | ||||
|         let layerControl = backgroundMapPicker; | ||||
|         if (layerSetup.flayers.length > 1) { | ||||
|         if (State.state.filteredLayers.data.length > 1) { | ||||
|             layerControl = new Combine([layerSelection, backgroundMapPicker]); | ||||
|         } | ||||
| 
 | ||||
|  | @ -120,14 +124,11 @@ export class InitUiElements { | |||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     static InitLayers(): { | ||||
|         minZoom: number | ||||
|         flayers: FilteredLayer[], | ||||
|         presets: Preset[] | ||||
|     } { | ||||
|         const addButtons: Preset[] = []; | ||||
| 
 | ||||
|     static InitLayers() { | ||||
| 
 | ||||
|         const flayers: FilteredLayer[] = [] | ||||
|         const presets: Preset[] = []; | ||||
| 
 | ||||
|         let minZoom = 0; | ||||
|         const state = State.state; | ||||
|  | @ -145,7 +146,6 @@ export class InitUiElements { | |||
| 
 | ||||
|             minZoom = Math.max(minZoom, layer.minzoom); | ||||
| 
 | ||||
|             const flayer = FilteredLayer.fromDefinition(layer, generateInfo); | ||||
| 
 | ||||
|             for (const preset of layer.presets ?? []) { | ||||
| 
 | ||||
|  | @ -160,25 +160,17 @@ export class InitUiElements { | |||
|                     preset.icon = layer.style(tags)?.icon?.iconUrl; | ||||
|                 } | ||||
| 
 | ||||
|                 const addButton = { | ||||
|                     name: preset.title, | ||||
|                     description: preset.description, | ||||
|                     icon: preset.icon, | ||||
|                     tags: preset.tags, | ||||
|                     layerToAddTo: flayer | ||||
|                 } | ||||
|                 addButtons.push(addButton); | ||||
|                 presets.push(preset); | ||||
|             } | ||||
| 
 | ||||
|             const flayer: FilteredLayer = FilteredLayer.fromDefinition(layer, generateInfo); | ||||
|             flayers.push(flayer); | ||||
|             flayer.isDisplayed.setData(true) | ||||
|         } | ||||
| 
 | ||||
|         State.state.filteredLayers.setData(flayers); | ||||
|         State.state.presets.setData(presets); | ||||
| 
 | ||||
|          | ||||
|         return { | ||||
|             minZoom: minZoom, | ||||
|             flayers: flayers, | ||||
|             presets: addButtons | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -1,13 +1,13 @@ | |||
| import {Layout} from "../Customizations/Layout"; | ||||
| import Translations from "../UI/i18n/Translations"; | ||||
| 
 | ||||
| export class CustomLayers extends Layout { | ||||
| export class CustomLayout extends Layout { | ||||
| 
 | ||||
|     public static NAME: string = "personal"; | ||||
| 
 | ||||
|     constructor() { | ||||
|         super( | ||||
|             CustomLayers.NAME, | ||||
|             CustomLayout.NAME, | ||||
|             ["en"], | ||||
|             Translations.t.favourite.title, | ||||
|             [], | ||||
|  |  | |||
|  | @ -9,22 +9,40 @@ import {CheckBox} from "../UI/Input/CheckBox"; | |||
| import {CustomLayersState} from "./CustomLayersState"; | ||||
| import {VerticalCombine} from "../UI/Base/VerticalCombine"; | ||||
| import {FixedUiElement} from "../UI/Base/FixedUiElement"; | ||||
| import {CustomLayout} from "./CustomLayers"; | ||||
| import {SubtleButton} from "../UI/Base/SubtleButton"; | ||||
| 
 | ||||
| export class CustomLayersPanel extends UIElement { | ||||
|     private checkboxes: UIElement[]; | ||||
|     private checkboxes: UIElement[] = []; | ||||
|      | ||||
|     private updateButton : UIElement; | ||||
| 
 | ||||
|     constructor() { | ||||
|         super(State.state.favourteLayers); | ||||
| 
 | ||||
|         this.ListenTo(State.state.osmConnection.userDetails); | ||||
| 
 | ||||
| 
 | ||||
|         const t = Translations.t.favourite; | ||||
| 
 | ||||
|         this.checkboxes = []; | ||||
|         const controls = new Map<string, UIEventSource<boolean>>(); | ||||
|         const favs = State.state.favourteLayers.data; | ||||
|          | ||||
|         this.updateButton = new SubtleButton("./assets/reload.svg", t.reload) | ||||
|             .onClick(() => { | ||||
|                 State.state.layerUpdater.ForceRefresh(); | ||||
|                 CustomLayersState.InitFavouriteLayers(State.state); | ||||
|                 State.state.layoutToUse.ping(); | ||||
|             }) | ||||
| 
 | ||||
|         const controls = new Map<string, UIEventSource<boolean>>(); | ||||
|         for (const layout of AllKnownLayouts.layoutsList) { | ||||
| 
 | ||||
|             if(layout.name === CustomLayout.NAME){ | ||||
|                 continue; | ||||
|             } | ||||
|             if (layout.hideFromOverview && State.state.osmConnection.userDetails.data.name !== "Pieter Vander Vennet") { | ||||
|                 continue | ||||
|             } | ||||
|              | ||||
|             const header = | ||||
|                 new Combine([ | ||||
|                     `<div class="custom-layer-panel-header-img"><img src='${layout.icon}'></div>`, | ||||
|  | @ -38,14 +56,23 @@ export class CustomLayersPanel extends UIElement { | |||
| 
 | ||||
|             for (const layer of layout.layers) { | ||||
|                 const image = (layer.icon ? `<img src='${layer.icon}'>` : Img.checkmark); | ||||
|                 const noimage = (layer.icon ? `<img src='${layer.icon}'>` : Img.no_checkmark); | ||||
| 
 | ||||
|                 const content = new Combine([ | ||||
|                     "<span>", | ||||
|                     "<b>", layer.name ?? "", "</b> ", | ||||
|                     layer.description !== undefined ? new Combine(["<br/>", layer.description]) : "", | ||||
|                     "</span>"]) | ||||
|                 const cb = new CheckBox( | ||||
|                     new Combine([ | ||||
|                         image, | ||||
|                         "<b>", layer.name ?? "", "</b> ", layer.description ?? "" | ||||
|                         image, content | ||||
|                     ]), | ||||
|                     new Combine([ | ||||
|                         "<span style='opacity: 0'>", | ||||
|                         image, "</span>", "<b>", layer.name ?? "", "</b> ", layer.description ?? "" | ||||
|                         "<span style='opacity: 0.1'>", | ||||
|                         noimage, "</span>",  | ||||
|                         "<del>", | ||||
|                         content, | ||||
|                         "</del>" | ||||
|                     ]), | ||||
|                     controls[layer.id] ?? (favs.indexOf(layer.id) >= 0) | ||||
|                 ); | ||||
|  | @ -64,30 +91,25 @@ export class CustomLayersPanel extends UIElement { | |||
| 
 | ||||
|             } | ||||
| 
 | ||||
|             State.state.favourteLayers.addCallback((layers) => { | ||||
|                 for (const layerId of layers) { | ||||
|                     controls[layerId]?.setData(true); | ||||
|                 } | ||||
|             }) | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         State.state.favourteLayers.addCallback((layers) => { | ||||
|             for (const layerId of layers) { | ||||
|                 controls[layerId].setData(true); | ||||
|             } | ||||
|         }) | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
|         const t = Translations.t.favourite; | ||||
|         const userDetails = State.state.osmConnection.userDetails.data; | ||||
|         if(!userDetails.loggedIn){ | ||||
|             return ""; | ||||
|             return t.loginNeeded.Render(); | ||||
|         } | ||||
| 
 | ||||
|         if(userDetails.csCount <= 100){ | ||||
|             return ""; | ||||
|         } | ||||
|          | ||||
|         return new VerticalCombine([ | ||||
|         return new Combine([ | ||||
|             t.panelIntro, | ||||
|            new FixedUiElement("<a href='./index.html?layout=personal'>GO</a>"), | ||||
|             this.updateButton, | ||||
|             ...this.checkboxes | ||||
|         ], "custom-layer-panel").Render(); | ||||
|     } | ||||
|  |  | |||
|  | @ -3,35 +3,29 @@ import {State} from "../State"; | |||
| export class CustomLayersState { | ||||
|     static RemoveFavouriteLayer(layer: string) { | ||||
| 
 | ||||
|         State.state.GetFilteredLayerFor(layer)?.isDisplayed?.setData(false); | ||||
|          | ||||
|         const favs = State.state.favourteLayers.data; | ||||
|         const ind = favs.indexOf(layer); | ||||
|         if (ind < 0) { | ||||
|             return; | ||||
|         } | ||||
|         console.log("REmovign fav layer", layer); | ||||
| 
 | ||||
|         favs.splice(ind, 1); | ||||
|         State.state.favourteLayers.ping(); | ||||
|         | ||||
| 
 | ||||
|         const osmConnection = State.state.osmConnection; | ||||
|         const count = osmConnection.GetPreference("mapcomplete-custom-layer-count"); | ||||
|         if (favs.length === 0) { | ||||
|             count.setData("0") | ||||
|         } else if (count.data === undefined || isNaN(Number(count.data))) { | ||||
|             count.data = "0"; | ||||
|         } | ||||
|         const lastId = Number(count.data); | ||||
| 
 | ||||
|         for (let i = 0; i < lastId; i++) { | ||||
|         for (let i = 0; i < favs.length; i++) { | ||||
|             const layerIDescr = osmConnection.GetPreference("mapcomplete-custom-layer-" + i); | ||||
|             if (layerIDescr.data === layer) { | ||||
|                 // We found the value to remove - mark with a tombstone
 | ||||
|                 layerIDescr.setData("-"); | ||||
|                 return; | ||||
|             } | ||||
|             layerIDescr.setData(favs[i]); | ||||
|         } | ||||
|         count.setData("" + favs.length) | ||||
|     } | ||||
| 
 | ||||
|     static AddFavouriteLayer(layer: string) { | ||||
|         State.state.GetFilteredLayerFor(layer)?.isDisplayed?.setData(true); | ||||
|          | ||||
|         const favs = State.state.favourteLayers.data; | ||||
|         const ind = favs.indexOf(layer); | ||||
|         if (ind >= 0) { | ||||
|  | @ -39,7 +33,6 @@ export class CustomLayersState { | |||
|         } | ||||
|         console.log("Adding fav layer", layer); | ||||
|         favs.push(layer); | ||||
|         State.state.favourteLayers.ping(); | ||||
| 
 | ||||
| 
 | ||||
|         const osmConnection = State.state.osmConnection; | ||||
|  | @ -51,7 +44,7 @@ export class CustomLayersState { | |||
| 
 | ||||
|         for (let i = 0; i < lastId; i++) { | ||||
|             const layerIDescr = osmConnection.GetPreference("mapcomplete-custom-layer-" + i); | ||||
|             if (layerIDescr.data === undefined || layerIDescr.data === "-") { | ||||
|             if (layerIDescr.data === undefined || layerIDescr.data === "") { | ||||
|                 // An earlier item was removed -> overwrite it
 | ||||
|                 layerIDescr.setData(layer); | ||||
|                 count.ping(); | ||||
|  | @ -65,12 +58,13 @@ export class CustomLayersState { | |||
|         count.setData((lastId + 1) + ""); | ||||
|     } | ||||
| 
 | ||||
|     static InitFavouriteLayer() { | ||||
|         const osmConnection = State.state.osmConnection; | ||||
|     static InitFavouriteLayers(state: State) { | ||||
|         const osmConnection = state.osmConnection; | ||||
|         const count = osmConnection.GetPreference("mapcomplete-custom-layer-count"); | ||||
|         const favs = State.state.favourteLayers.data; | ||||
|         const favs = state.favourteLayers.data; | ||||
|         let changed = false; | ||||
|         count.addCallback((countStr) => { | ||||
|             console.log("UPdating favourites") | ||||
|             if (countStr === undefined) { | ||||
|                 return; | ||||
|             } | ||||
|  | @ -80,13 +74,13 @@ export class CustomLayersState { | |||
|             } | ||||
|             for (let i = 0; i < countI; i++) { | ||||
|                 const layerId = osmConnection.GetPreference("mapcomplete-custom-layer-" + i).data; | ||||
|                 if (layerId !== undefined && layerId !== "-" && favs.indexOf(layerId) < 0) { | ||||
|                     State.state.favourteLayers.data.push(layerId); | ||||
|                 if (layerId !== undefined && layerId !== "" && favs.indexOf(layerId) < 0) { | ||||
|                     state.favourteLayers.data.push(layerId); | ||||
|                     changed = true; | ||||
|                 } | ||||
|             } | ||||
|             if (changed) { | ||||
|                 State.state.favourteLayers.ping(); | ||||
|                 state.favourteLayers.ping(); | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
|  |  | |||
|  | @ -7,9 +7,8 @@ import {Basemap} from "./Leaflet/Basemap"; | |||
| import {State} from "../State"; | ||||
| 
 | ||||
| export class LayerUpdater { | ||||
|     private _layers: FilteredLayer[]; | ||||
|     private widenFactor: number; | ||||
| 
 | ||||
|     public readonly sufficentlyZoomed: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
|     public readonly runningQuery: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
|     public readonly retries: UIEventSource<number> = new UIEventSource<number>(0); | ||||
|      /** | ||||
|  | @ -17,8 +16,6 @@ export class LayerUpdater { | |||
|      */ | ||||
|     private previousBounds: Bounds; | ||||
| 
 | ||||
|     private _overpass: Overpass; | ||||
|     private _minzoom: number; | ||||
| 
 | ||||
|     /** | ||||
|      * The most important layer should go first, as that one gets first pick for the questions | ||||
|  | @ -26,28 +23,39 @@ export class LayerUpdater { | |||
|      * @param minzoom | ||||
|      * @param layers | ||||
|      */ | ||||
|     constructor(minzoom: number, | ||||
|                 widenFactor: number, | ||||
|                 layers: FilteredLayer[]) { | ||||
|         this.widenFactor = widenFactor; | ||||
|         this._layers = layers; | ||||
|         this._minzoom = minzoom; | ||||
|         var filters: TagsFilter[] = []; | ||||
|         for (const layer of layers) { | ||||
|             filters.push(layer.filters); | ||||
|         } | ||||
|         this._overpass = new Overpass(new Or(filters)); | ||||
|     constructor(state: State) { | ||||
| 
 | ||||
|         const self = this; | ||||
|         State.state.locationControl.addCallback(function () { | ||||
|             self.update(); | ||||
|         state.locationControl.addCallback(() => { | ||||
|             self.update(state) | ||||
|         }); | ||||
|         state.layoutToUse.addCallback(() => { | ||||
|             self.update(state) | ||||
|         }); | ||||
|         self.update(); | ||||
|         | ||||
|         self.update(state); | ||||
|     } | ||||
| 
 | ||||
|     private GetFilter(state: State) { | ||||
|         var filters: TagsFilter[] = []; | ||||
|         state = state ?? State.state; | ||||
|         for (const layer of state.layoutToUse.data.layers) { | ||||
|             if (state.locationControl.data.zoom < layer.minzoom) { | ||||
|                 return undefined; | ||||
|             } | ||||
|             filters.push(layer.overpassFilter); | ||||
|         } | ||||
|         if (filters.length === 0) { | ||||
|             return undefined; | ||||
|         } | ||||
|         return new Or(filters); | ||||
|     } | ||||
| 
 | ||||
|     private handleData(geojson: any) { | ||||
|         const self = this; | ||||
| 
 | ||||
|         self.retries.setData(0); | ||||
|          | ||||
|         function renderLayers(layers: FilteredLayer[]) { | ||||
|             if (layers.length === 0) { | ||||
|                 self.runningQuery.setData(false); | ||||
|  | @ -65,69 +73,79 @@ export class LayerUpdater { | |||
|                 renderLayers(rest); | ||||
|             }, 50) | ||||
|         } | ||||
|         renderLayers(this._layers); | ||||
| 
 | ||||
|         renderLayers(State.state.filteredLayers.data); | ||||
|     } | ||||
| 
 | ||||
|     private handleFail(reason: any) { | ||||
|     private handleFail(state: State, reason: any) { | ||||
|         this.retries.data++; | ||||
|         this.ForceRefresh(); | ||||
|         console.log(`QUERY FAILED (retrying in ${5 * this.retries.data} sec)`, reason); | ||||
|         this.previousBounds = undefined; | ||||
|         this.retries.data ++; | ||||
|         this.retries.ping(); | ||||
|         const self = this; | ||||
|         window?.setTimeout( | ||||
|             function(){self.update()}, this.retries.data * 5000 | ||||
|             function () { | ||||
|                 self.update(state) | ||||
|             }, this.retries.data * 5000 | ||||
|         ) | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private update(): void { | ||||
|         if (this.IsInBounds()) { | ||||
|     private update(state: State): void { | ||||
|         if (this.IsInBounds(state)) { | ||||
|             return; | ||||
|         } | ||||
|         console.log("Zoom level: ",State.state.bm.map.getZoom(), "Least needed zoom:", this._minzoom) | ||||
|         if (State.state.bm.map.getZoom() < this._minzoom || State.state.bm.Location.data.zoom < this._minzoom) { | ||||
| 
 | ||||
| 
 | ||||
|         const filter = this.GetFilter(state); | ||||
| 
 | ||||
| 
 | ||||
|         this.sufficentlyZoomed.setData(filter !== undefined); | ||||
|         if (filter === undefined) { | ||||
|             console.log("Zoom insufficient to run query") | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (this.runningQuery.data) { | ||||
|             console.log("Still running a query, skip"); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const bounds = State.state.bm.map.getBounds(); | ||||
|         const bounds = state.bm.map.getBounds(); | ||||
| 
 | ||||
|         const diff = this.widenFactor; | ||||
|         const diff = state.layoutToUse.data.widenFactor; | ||||
| 
 | ||||
|         const n = bounds.getNorth() + diff; | ||||
|         const e = bounds.getEast() +  diff; | ||||
|         const e = bounds.getEast() + diff; | ||||
|         const s = bounds.getSouth() - diff; | ||||
|         const w = bounds.getWest() -  diff; | ||||
|         const w = bounds.getWest() - diff; | ||||
| 
 | ||||
|         this.previousBounds = {north: n, east: e, south: s, west: w}; | ||||
| 
 | ||||
|         this.runningQuery.setData(true); | ||||
|         const self = this; | ||||
|         this._overpass.queryGeoJson(this.previousBounds, | ||||
|         const overpass = new Overpass(filter); | ||||
|         overpass.queryGeoJson(this.previousBounds, | ||||
|             function (data) { | ||||
|                 self.handleData(data) | ||||
|             }, | ||||
|             function (reason) { | ||||
|                 self.handleFail(reason) | ||||
|                 self.handleFail(state, reason) | ||||
|             } | ||||
|         ); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     private IsInBounds(): boolean { | ||||
|     private IsInBounds(state: State): boolean { | ||||
| 
 | ||||
|         if (this.previousBounds === undefined) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         const b = State.state.bm.map.getBounds(); | ||||
|         const b = state.bm.map.getBounds(); | ||||
|         if (b.getSouth() < this.previousBounds.south) { | ||||
|             return false; | ||||
|         } | ||||
|  | @ -146,4 +164,8 @@ export class LayerUpdater { | |||
|         return true; | ||||
|     } | ||||
|      | ||||
|     public ForceRefresh(){ | ||||
|         this.previousBounds = undefined; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -1,13 +1,17 @@ | |||
| import {UIEventSource} from "../UI/UIEventSource"; | ||||
| import {UIElement} from "../UI/UIElement"; | ||||
| 
 | ||||
| export class LocalStorageSource { | ||||
| 
 | ||||
|     static Get(key: string, defaultValue: string = undefined): UIEventSource<string> { | ||||
| 
 | ||||
|         //*
 | ||||
|         if (UIElement.runningFromConsole) { | ||||
| 
 | ||||
|             // ignore when running from the console
 | ||||
|             return new UIEventSource<string>(defaultValue); | ||||
|             /*/ | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         const saved = localStorage.getItem(key); | ||||
|         const source = new UIEventSource<string>(saved ?? defaultValue); | ||||
| 
 | ||||
|  | @ -16,6 +20,5 @@ export class LocalStorageSource { | |||
|             console.log("Wriging ", key, data) | ||||
|         }); | ||||
|         return source; | ||||
|         //*/
 | ||||
|     } | ||||
| } | ||||
|  | @ -303,12 +303,13 @@ export class Changes { | |||
| 
 | ||||
|         const millisTillChangesAreSaved = state.secondsTillChangesAreSaved; | ||||
|         const saveAfterXMillis = state.secondsTillChangesAreSaved.data * 1000; | ||||
|         const self= this; | ||||
|         this.pendingChangesES.addCallback(function () { | ||||
| 
 | ||||
|             var c = this.pendingChangesES.data; | ||||
|             var c = self.pendingChangesES.data; | ||||
|             if (c > 10) { | ||||
|                 millisTillChangesAreSaved.setData(0); | ||||
|                 this.uploadAll(undefined); | ||||
|                 self.uploadAll(undefined); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|  | @ -319,8 +320,8 @@ export class Changes { | |||
|         }); | ||||
| 
 | ||||
|         millisTillChangesAreSaved.addCallback((time) => { | ||||
|                 if (time <= 0 && this.pendingChangesES.data > 0) { | ||||
|                     this.uploadAll(undefined); | ||||
|                 if (time <= 0 && self.pendingChangesES.data > 0) { | ||||
|                     self.uploadAll(undefined); | ||||
|                 } | ||||
|             } | ||||
|         ) | ||||
|  |  | |||
|  | @ -216,7 +216,6 @@ export class OsmConnection { | |||
|                 self.preferences.data[k] = v; | ||||
|             } | ||||
|             self.preferences.ping(); | ||||
|             CustomLayersState.InitFavouriteLayer(); | ||||
|         }); | ||||
|     } | ||||
|      | ||||
|  | @ -234,6 +233,24 @@ export class OsmConnection { | |||
| 
 | ||||
|         this.preferences.data[k] = v; | ||||
|         this.preferences.ping(); | ||||
|          | ||||
|         if(v === ""){ | ||||
|             this.auth.xhr({ | ||||
|                 method: 'DELETE', | ||||
|                 path: '/api/0.6/user/preferences/' + k, | ||||
|                 options: {header: {'Content-Type': 'text/plain'}}, | ||||
|             }, function (error, result) { | ||||
|                 if (error) { | ||||
|                     console.log("Could not remove preference", error); | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 console.log("Preference removed!", result == "" ? "OK" : result); | ||||
| 
 | ||||
|             }); | ||||
|         } | ||||
|          | ||||
|          | ||||
|         this.auth.xhr({ | ||||
|             method: 'PUT', | ||||
|             path: '/api/0.6/user/preferences/' + k, | ||||
|  |  | |||
							
								
								
									
										22
									
								
								State.ts
									
										
									
									
									
								
							
							
						
						
									
										22
									
								
								State.ts
									
										
									
									
									
								
							|  | @ -4,7 +4,7 @@ import {QueryParameters} from "./Logic/QueryParameters"; | |||
| import {LocalStorageSource} from "./Logic/LocalStorageSource"; | ||||
| import {Layout} from "./Customizations/Layout"; | ||||
| import {Utils} from "./Utils"; | ||||
| import {LayerDefinition} from "./Customizations/LayerDefinition"; | ||||
| import {LayerDefinition, Preset} from "./Customizations/LayerDefinition"; | ||||
| import {ElementStorage} from "./Logic/ElementStorage"; | ||||
| import {Changes} from "./Logic/Osm/Changes"; | ||||
| import {Basemap} from "./Logic/Leaflet/Basemap"; | ||||
|  | @ -13,6 +13,8 @@ import Locale from "./UI/i18n/Locale"; | |||
| import {VariableUiElement} from "./UI/Base/VariableUIElement"; | ||||
| import Translations from "./UI/i18n/Translations"; | ||||
| import {CustomLayersState} from "./Logic/CustomLayersState"; | ||||
| import {FilteredLayer} from "./Logic/FilteredLayer"; | ||||
| import {LayerUpdater} from "./Logic/LayerUpdater"; | ||||
| 
 | ||||
| /** | ||||
|  * Contains the global state: a bunch of UI-event sources | ||||
|  | @ -46,6 +48,12 @@ export class State { | |||
|      */ | ||||
|     public osmConnection: OsmConnection; | ||||
|      | ||||
|     public layerUpdater : LayerUpdater; | ||||
|      | ||||
|      | ||||
|     public filteredLayers: UIEventSource<FilteredLayer[]> = new UIEventSource<FilteredLayer[]>([]) | ||||
|     public presets: UIEventSource<Preset[]> = new UIEventSource<Preset[]>([]) | ||||
|      | ||||
|     /** | ||||
|      *  The message that should be shown at the center of the screen | ||||
|      */ | ||||
|  | @ -144,6 +152,7 @@ export class State { | |||
|             QueryParameters.GetQueryParameter("oauth_token", undefined) | ||||
|         ); | ||||
|          | ||||
|         CustomLayersState.InitFavouriteLayers(this); | ||||
|         | ||||
|         Locale.language.syncWith(this.osmConnection.GetPreference("language")); | ||||
| 
 | ||||
|  | @ -190,6 +199,17 @@ export class State { | |||
| 
 | ||||
|             }) | ||||
|         )); | ||||
|         this.layerUpdater = new LayerUpdater(this); | ||||
| 
 | ||||
|     } | ||||
|      | ||||
|     public GetFilteredLayerFor(id: string) : FilteredLayer{ | ||||
|         for (const flayer of this.filteredLayers.data) { | ||||
|             console.log(flayer.layerDef.id, id) | ||||
|             if(flayer.layerDef.id === id){ | ||||
|                 return flayer; | ||||
|             } | ||||
|         } | ||||
|         return undefined; | ||||
|     } | ||||
| } | ||||
|  | @ -6,31 +6,30 @@ import {State} from "../State"; | |||
| 
 | ||||
| export class CenterMessageBox extends UIElement { | ||||
| 
 | ||||
|     private readonly _queryRunning: UIEventSource<boolean>; | ||||
|     private startZoom: number; | ||||
| 
 | ||||
|     constructor( | ||||
|         startZoom: number, | ||||
|         queryRunning: UIEventSource<boolean> | ||||
|     ) { | ||||
|         super(State.state.centerMessage); | ||||
|         this.startZoom = startZoom; | ||||
| 
 | ||||
|         this.ListenTo(State.state.locationControl); | ||||
|         this.ListenTo(queryRunning); | ||||
| 
 | ||||
|         this._queryRunning = queryRunning; | ||||
| 
 | ||||
| 
 | ||||
|         this.ListenTo(State.state.layerUpdater.retries); | ||||
|         this.ListenTo(State.state.layerUpdater.runningQuery); | ||||
|         this.ListenTo(State.state.layerUpdater.sufficentlyZoomed); | ||||
|     } | ||||
| 
 | ||||
|     private prep(): { innerHtml: string, done: boolean } { | ||||
|         if (State.state.centerMessage.data != "") { | ||||
|             return {innerHtml: State.state.centerMessage.data, done: false}; | ||||
|         } | ||||
|         if (this._queryRunning.data) { | ||||
|         const lu = State.state.layerUpdater; | ||||
|         if(lu.retries.data > 0) { | ||||
|             return {innerHtml: Translations.t.centerMessage.retrying.Subs({count: ""+ lu.retries.data}).Render(), done: false}; | ||||
|         } | ||||
|          | ||||
|         if (lu.runningQuery.data) { | ||||
|             return {innerHtml: Translations.t.centerMessage.loadingData.Render(), done: false}; | ||||
|         } else if (State.state.locationControl.data.zoom < this.startZoom) { | ||||
|              | ||||
|         }  | ||||
|         if (!lu.sufficentlyZoomed.data) { | ||||
|             return {innerHtml: Translations.t.centerMessage.zoomIn.Render(), done: false}; | ||||
|         } else { | ||||
|             return {innerHtml: Translations.t.centerMessage.ready.Render(), done: true}; | ||||
|  |  | |||
|  | @ -4,26 +4,27 @@ import { CheckBox } from "./Input/CheckBox"; | |||
| import Combine from "./Base/Combine"; | ||||
| import {Utils} from "../Utils"; | ||||
| import {Img} from "./Img"; | ||||
| import {State} from "../State"; | ||||
| 
 | ||||
| export class LayerSelection extends UIElement{ | ||||
| export class LayerSelection extends UIElement { | ||||
| 
 | ||||
|     private readonly _checkboxes: UIElement[]; | ||||
| 
 | ||||
|     constructor(layers: FilteredLayer[]) { | ||||
|       super(undefined); | ||||
|       this._checkboxes = []; | ||||
|     constructor() { | ||||
|         super(undefined); | ||||
|         this._checkboxes = []; | ||||
| 
 | ||||
|       for (const layer of layers) { | ||||
|           const checkbox = `<svg width="26" height="18" viewBox="0 0 26 18" fill="none" xmlns="http://www.w3.org/2000/svg">
 | ||||
|         for (const layer of State.state.filteredLayers.data) { | ||||
|             const checkbox = `<svg width="26" height="18" viewBox="0 0 26 18" fill="none" xmlns="http://www.w3.org/2000/svg">
 | ||||
|             <path d="M3 7.28571L10.8261 15L23 3" stroke="#003B8B" stroke-width="4" stroke-linejoin="round"/> | ||||
|             </svg>`;
 | ||||
|           let icon = "<img >"; | ||||
|           if (layer.layerDef.icon && layer.layerDef.icon !== "") { | ||||
|               icon = `<img width="20" height="20" src="${layer.layerDef.icon}" alt="">` | ||||
|           } | ||||
|           const name = layer.layerDef.name; | ||||
|             let icon = "<img >"; | ||||
|             if (layer.layerDef.icon && layer.layerDef.icon !== "") { | ||||
|                 icon = `<img width="20" height="20" src="${layer.layerDef.icon}" alt="">` | ||||
|             } | ||||
|             const name = layer.layerDef.name; | ||||
| 
 | ||||
|           this._checkboxes.push(new CheckBox( | ||||
|             this._checkboxes.push(new CheckBox( | ||||
|               new Combine([checkbox, icon, name]), | ||||
|               new Combine([ | ||||
|                   Img.no_checkmark, | ||||
|  |  | |||
|  | @ -10,12 +10,14 @@ import {VariableUiElement} from "./Base/VariableUIElement"; | |||
| import Combine from "./Base/Combine"; | ||||
| import {SubtleButton} from "./Base/SubtleButton"; | ||||
| import {State} from "../State"; | ||||
| import {CustomLayout} from "../Logic/CustomLayers"; | ||||
| 
 | ||||
| 
 | ||||
| export class MoreScreen extends UIElement { | ||||
| 
 | ||||
|     constructor() { | ||||
|         super(State.state.locationControl); | ||||
|         this.ListenTo(State.state.osmConnection.userDetails); | ||||
|     } | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
|  | @ -24,12 +26,20 @@ export class MoreScreen extends UIElement { | |||
|         const els: UIElement[] = [] | ||||
|         for (const k in AllKnownLayouts.allSets) { | ||||
|             const layout = AllKnownLayouts.allSets[k] | ||||
|             if (layout.hideFromOverview) { | ||||
|             if (layout.hideFromOverview && State.state.osmConnection.userDetails.data.name !== "Pieter Vander Vennet") { | ||||
|                 continue | ||||
|             } | ||||
|             if (layout.name === State.state.layoutToUse.data.name) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (layout.name === CustomLayout.NAME) { | ||||
|                 if (!State.state.osmConnection.userDetails.data.loggedIn) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (State.state.osmConnection.userDetails.data.csCount < 100) { | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             const currentLocation = State.state.locationControl.data; | ||||
|             const linkText = | ||||
|  |  | |||
|  | @ -13,13 +13,6 @@ import {Changes} from "../Logic/Osm/Changes"; | |||
| import {UserDetails} from "../Logic/Osm/OsmConnection"; | ||||
| import {State} from "../State"; | ||||
| 
 | ||||
| export interface Preset { | ||||
|     description: string | UIElement, | ||||
|     name: string | UIElement, | ||||
|     icon: string, | ||||
|     tags: Tag[], | ||||
|     layerToAddTo: FilteredLayer | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Asks to add a feature at the last clicked location, at least if zoom is sufficient | ||||
|  | @ -27,57 +20,68 @@ export interface Preset { | |||
| export class SimpleAddUI extends UIElement { | ||||
|     private _addButtons: UIElement[]; | ||||
|      | ||||
|     private _dataIsLoading: UIEventSource<boolean>; | ||||
| 
 | ||||
|     private _confirmPreset: UIEventSource<Preset> | ||||
|         = new UIEventSource<Preset>(undefined); | ||||
|     private _confirmPreset: UIEventSource<{ | ||||
|         description: string | UIElement, | ||||
|         name: string | UIElement, | ||||
|         icon: string, | ||||
|         tags: Tag[], | ||||
|         layerToAddTo: FilteredLayer | ||||
|     }> | ||||
|         = new UIEventSource(undefined); | ||||
|     private confirmButton: UIElement = undefined; | ||||
|     private cancelButton: UIElement; | ||||
|     private goToInboxButton: UIElement = new SubtleButton("./assets/envelope.svg",  | ||||
|         Translations.t.general.goToInbox, {url:"https://www.openstreetmap.org/messages/inbox", newTab: false}); | ||||
| 
 | ||||
|     constructor( | ||||
|                 dataIsLoading: UIEventSource<boolean>, | ||||
|                 addButtons: { description: string | UIElement, name: string | UIElement; icon: string; tags: Tag[]; layerToAddTo: FilteredLayer }[], | ||||
|     ) { | ||||
|     constructor() { | ||||
|         super(State.state.locationControl); | ||||
|         this.ListenTo(Locale.language); | ||||
|         this.ListenTo(State.state.osmConnection.userDetails); | ||||
| 
 | ||||
|         this._dataIsLoading = dataIsLoading; | ||||
|         this.ListenTo(dataIsLoading); | ||||
|         this.ListenTo(State.state.layerUpdater.runningQuery); | ||||
| 
 | ||||
|         this._addButtons = []; | ||||
|         this.ListenTo(this._confirmPreset); | ||||
|         this.clss = "add-ui" | ||||
| 
 | ||||
|         const self = this; | ||||
|         for (const option of addButtons) { | ||||
|             // <button type='button'> looks SO retarded
 | ||||
|             // the default type of button is 'submit', which performs a POST and page reload
 | ||||
|             const button = | ||||
|                 new SubtleButton( | ||||
|                     option.icon, | ||||
|                     new Combine([ | ||||
|                         "<b>", | ||||
|                         option.name, | ||||
|                         "</b><br/>", | ||||
|                         option.description !== undefined ? option.description : ""]) | ||||
|                 ).onClick( | ||||
|                     () => { | ||||
|                         self.confirmButton = new SubtleButton(option.icon, | ||||
|                             new Combine([ | ||||
|                                 "<b>", | ||||
|                                 option.name, | ||||
|                                 "</b><br/>", | ||||
|                                 option.description !== undefined ? option.description : ""])); | ||||
|                         self.confirmButton.onClick(self.CreatePoint(option)); | ||||
|                         self._confirmPreset.setData(option); | ||||
|                     } | ||||
|         for (const layer of State.state.filteredLayers.data) { | ||||
|             for (const preset of layer.layerDef.presets) { | ||||
| 
 | ||||
| 
 | ||||
|                 // <button type='button'> looks SO retarded
 | ||||
|                 // the default type of button is 'submit', which performs a POST and page reload
 | ||||
|                 const button = | ||||
|                     new SubtleButton( | ||||
|                         preset.icon, | ||||
|                         new Combine([ | ||||
|                             "<b>", | ||||
|                             preset.title, | ||||
|                             "</b><br/>", | ||||
|                             preset.description !== undefined ? preset.description : ""]) | ||||
|                     ).onClick( | ||||
|                         () => { | ||||
|                             self.confirmButton = new SubtleButton(preset.icon, | ||||
|                                 new Combine([ | ||||
|                                     "Add a ", | ||||
|                                     "<b>", | ||||
|                                     preset.title, | ||||
|                                     "</b> here<br/>", | ||||
|                                     preset.description !== undefined ? preset.description : ""])); | ||||
|                             self.confirmButton.onClick(self.CreatePoint(preset.tags, layer)); | ||||
|                             self._confirmPreset.setData({ | ||||
|                                 tags: preset.tags, | ||||
|                                 layerToAddTo: layer, | ||||
|                                 name: preset.title, | ||||
|                                 description: preset.description, | ||||
|                                 icon: preset.icon | ||||
|                             }); | ||||
|                         } | ||||
|                 ) | ||||
| 
 | ||||
| 
 | ||||
|             this._addButtons.push(button); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         this.cancelButton = new SubtleButton( | ||||
|  | @ -88,13 +92,13 @@ export class SimpleAddUI extends UIElement { | |||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     private CreatePoint(option: { tags: Tag[]; layerToAddTo: FilteredLayer }) { | ||||
|     private CreatePoint(tags: Tag[], layerToAddTo: FilteredLayer) { | ||||
|         const self = this; | ||||
|         return () => { | ||||
| 
 | ||||
|             const loc = State.state.bm.LastClickLocation.data; | ||||
|             let feature = State.state.changes.createElement(option.tags, loc.lat, loc.lon); | ||||
|             option.layerToAddTo.AddNewElement(feature); | ||||
|             let feature = State.state.changes.createElement(tags, loc.lat, loc.lon); | ||||
|             layerToAddTo.AddNewElement(feature); | ||||
|             State.state.selectedElement.setData({feature: feature}); | ||||
|         } | ||||
|     } | ||||
|  | @ -106,7 +110,7 @@ export class SimpleAddUI extends UIElement { | |||
|         if (this._confirmPreset.data !== undefined) { | ||||
| 
 | ||||
|             if(userDetails.data.dryRun){ | ||||
|                 this.CreatePoint(this._confirmPreset.data)(); | ||||
|                 this.CreatePoint(this._confirmPreset.data.tags, this._confirmPreset.data.layerToAddTo)(); | ||||
|                 return ""; | ||||
|             } | ||||
| 
 | ||||
|  | @ -159,7 +163,7 @@ export class SimpleAddUI extends UIElement { | |||
|             return new Combine([header, Translations.t.general.add.zoomInFurther]).Render() | ||||
|         } | ||||
| 
 | ||||
|         if (this._dataIsLoading.data) { | ||||
|         if (State.state.layerUpdater.runningQuery.data) { | ||||
|             return new Combine([header, Translations.t.general.add.stillLoading]).Render() | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -123,5 +123,9 @@ export class UserBadge extends UIElement { | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     protected InnerUpdate(htmlElement: HTMLElement) { | ||||
|         State.state.osmConnection.registerActivateOsmAUthenticationClass(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  | @ -9,7 +9,7 @@ export default class Translation extends UIElement { | |||
| 
 | ||||
|     private static forcedLanguage = undefined; | ||||
| 
 | ||||
|     public Subs(text: any /*Map<string, string | UIElement>*/) { | ||||
|     public Subs(text: any) { | ||||
|         const newTranslations = {}; | ||||
|         for (const lang in this.translations) { | ||||
|             let template: string = this.translations[lang]; | ||||
|  |  | |||
|  | @ -701,6 +701,8 @@ export default class Translations { | |||
|                 fr: 'Rapprochez vous sur la carte pour voir ou éditer les données', | ||||
|             }), | ||||
|             ready: new T({en: 'Done!', nl: 'Klaar!', fr: 'Finis!'}),  | ||||
|             retrying: new T({en: "Loading data failed. Trying again... ({count})"}) | ||||
| 
 | ||||
|         }, | ||||
|         general: { | ||||
|             loginWithOpenStreetMap: new T({en: "Login with OpenStreetMap", nl: "Aanmelden met OpenStreetMap", fr:'Se connecter avec OpenStreeMap'}), | ||||
|  | @ -959,9 +961,10 @@ export default class Translations { | |||
|                 en: "<h3>Your custom theme</h3>In your custom theme, you can add some favourite layers from other themes to create a custom theme." | ||||
|             }), | ||||
|             panelIntro: new T({ | ||||
|                 en:"<h3>Your custom theme</h3>Create your own theme here by picking your favourite layers" | ||||
|             }) | ||||
| 
 | ||||
|                 en: "<h3>Your custom theme</h3>Create your own theme here by picking your favourite layers" | ||||
|             }), | ||||
|             loginNeeded: new T({en: "<h3>Log in</h3>A custom layout is only available for OpenStreetMap users"}), | ||||
|             reload: new T({en: "Reload the data"}) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										54
									
								
								assets/reload.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								assets/reload.svg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,54 @@ | |||
| <?xml version="1.0" encoding="iso-8859-1"?> | ||||
| <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> | ||||
| <svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 width="487.23px" height="487.23px" viewBox="0 0 487.23 487.23" style="enable-background:new 0 0 487.23 487.23;" | ||||
| 	 xml:space="preserve"> | ||||
| <g> | ||||
| 	<g> | ||||
| 		<path d="M55.323,203.641c15.664,0,29.813-9.405,35.872-23.854c25.017-59.604,83.842-101.61,152.42-101.61 | ||||
| 			c37.797,0,72.449,12.955,100.23,34.442l-21.775,3.371c-7.438,1.153-13.224,7.054-14.232,14.512 | ||||
| 			c-1.01,7.454,3.008,14.686,9.867,17.768l119.746,53.872c5.249,2.357,11.33,1.904,16.168-1.205 | ||||
| 			c4.83-3.114,7.764-8.458,7.796-14.208l0.621-131.943c0.042-7.506-4.851-14.144-12.024-16.332 | ||||
| 			c-7.185-2.188-14.947,0.589-19.104,6.837l-16.505,24.805C370.398,26.778,310.1,0,243.615,0C142.806,0,56.133,61.562,19.167,149.06 | ||||
| 			c-5.134,12.128-3.84,26.015,3.429,36.987C29.865,197.023,42.152,203.641,55.323,203.641z"/> | ||||
| 		<path d="M464.635,301.184c-7.27-10.977-19.558-17.594-32.728-17.594c-15.664,0-29.813,9.405-35.872,23.854 | ||||
| 			c-25.018,59.604-83.843,101.61-152.42,101.61c-37.798,0-72.45-12.955-100.232-34.442l21.776-3.369 | ||||
| 			c7.437-1.153,13.223-7.055,14.233-14.514c1.009-7.453-3.008-14.686-9.867-17.768L49.779,285.089 | ||||
| 			c-5.25-2.356-11.33-1.905-16.169,1.205c-4.829,3.114-7.764,8.458-7.795,14.207l-0.622,131.943 | ||||
| 			c-0.042,7.506,4.85,14.144,12.024,16.332c7.185,2.188,14.948-0.59,19.104-6.839l16.505-24.805 | ||||
| 			c44.004,43.32,104.303,70.098,170.788,70.098c100.811,0,187.481-61.561,224.446-149.059 | ||||
| 			C473.197,326.043,471.903,312.157,464.635,301.184z"/> | ||||
| 	</g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.9 KiB | 
							
								
								
									
										11
									
								
								index.css
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								index.css
									
										
									
									
									
								
							|  | @ -1168,10 +1168,7 @@ form { | |||
|     background-color: white; | ||||
|     z-index: 5001; | ||||
|     box-shadow: 0 0 10px black; | ||||
|     border-bottom: 1px solid white; | ||||
|     border-left: 1px solid black; | ||||
|     border-right:1px solid black; | ||||
|     border-top: 1px solid black; | ||||
|     border: 1px solid white; | ||||
| } | ||||
| 
 | ||||
| .tab-non-active { | ||||
|  | @ -1270,23 +1267,23 @@ form { | |||
| } | ||||
| 
 | ||||
| .custom-layer-panel-header-img { | ||||
|     width: 4em; | ||||
|     min-width: 4em; | ||||
|     height: 4em; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| .custom-layer-checkbox { | ||||
|     font-size: larger; | ||||
|     height: 2em; | ||||
|     min-height: 2em; | ||||
|     background-color: #e5f5ff; | ||||
|     margin:0.3em; | ||||
|     margin-left: 2em; | ||||
|     display: flex; | ||||
|     justify-content: flex-start; | ||||
|     align-items: stretch; | ||||
|     text-decoration: none; | ||||
|     padding: 0.5em; | ||||
|     border-radius: 1em; | ||||
|     width: unset; | ||||
| } | ||||
| .custom-layer-checkbox img { | ||||
|     max-width: 1.5em; | ||||
|  |  | |||
							
								
								
									
										26
									
								
								index.ts
									
										
									
									
									
								
							
							
						
						
									
										26
									
								
								index.ts
									
										
									
									
									
								
							|  | @ -1,7 +1,6 @@ | |||
| import {UserBadge} from "./UI/UserBadge"; | ||||
| import {CenterMessageBox} from "./UI/CenterMessageBox"; | ||||
| import {TagUtils} from "./Logic/TagsFilter"; | ||||
| import {LayerUpdater} from "./Logic/LayerUpdater"; | ||||
| import {FullScreenMessageBoxHandler} from "./UI/FullScreenMessageBoxHandler"; | ||||
| import {FeatureInfoBox} from "./UI/FeatureInfoBox"; | ||||
| import {SimpleAddUI} from "./UI/SimpleAddUI"; | ||||
|  | @ -14,8 +13,7 @@ import {InitUiElements} from "./InitUiElements"; | |||
| import {StrayClickHandler} from "./Logic/Leaflet/StrayClickHandler"; | ||||
| import {GeoLocationHandler} from "./Logic/Leaflet/GeoLocationHandler"; | ||||
| import {State} from "./State"; | ||||
| import {All} from "./Customizations/Layouts/All"; | ||||
| import {CustomLayers} from "./Logic/CustomLayers"; | ||||
| import {CustomLayout} from "./Logic/CustomLayers"; | ||||
| 
 | ||||
| 
 | ||||
| // --------------------- Special actions based on the parameters -----------------
 | ||||
|  | @ -78,10 +76,8 @@ function setupAllLayerElements() { | |||
| 
 | ||||
| // ------------- Setup the layers -------------------------------
 | ||||
| 
 | ||||
|     const layerSetup = InitUiElements.InitLayers(); | ||||
| 
 | ||||
|     const layerUpdater = new LayerUpdater(layerSetup.minZoom, layoutToUse.widenFactor, layerSetup.flayers); | ||||
|     InitUiElements.InitLayerSelection(layerSetup) | ||||
|     InitUiElements.InitLayers(); | ||||
|     InitUiElements.InitLayerSelection(); | ||||
| 
 | ||||
| 
 | ||||
| // ------------------ Setup various other UI elements ------------
 | ||||
|  | @ -89,31 +85,29 @@ function setupAllLayerElements() { | |||
| 
 | ||||
|     InitUiElements.OnlyIf(State.state.featureSwitchAddNew, () => { | ||||
|         new StrayClickHandler(() => { | ||||
|                 return new SimpleAddUI( | ||||
|                     layerUpdater.runningQuery, | ||||
|                     layerSetup.presets); | ||||
|                 return new SimpleAddUI(); | ||||
|             } | ||||
|         ); | ||||
|     }); | ||||
| 
 | ||||
|     new CenterMessageBox( | ||||
|         layerSetup.minZoom, | ||||
|         layerUpdater.runningQuery) | ||||
|         .AttachTo("centermessage"); | ||||
|     new CenterMessageBox()        .AttachTo("centermessage"); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| setupAllLayerElements(); | ||||
| 
 | ||||
| if (layoutToUse === AllKnownLayouts.allSets[CustomLayers.NAME]) { | ||||
| if (layoutToUse === AllKnownLayouts.allSets[CustomLayout.NAME]) { | ||||
|     State.state.favourteLayers.addCallback((favs) => { | ||||
|         layoutToUse.layers = []; | ||||
|         for (const fav of favs) { | ||||
|             const layer = AllKnownLayouts.allLayers[fav]; | ||||
|             if (!!layer) { | ||||
|                 layoutToUse.layers.push(layer); | ||||
|             } | ||||
|             setupAllLayerElements(); | ||||
|         } | ||||
|         }; | ||||
|         State.state.locationControl.ping(); | ||||
|          | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue