forked from MapComplete/MapComplete
		
	More refactoring!
This commit is contained in:
		
							parent
							
								
									b2c234b51d
								
							
						
					
					
						commit
						6ac8ec84e4
					
				
					 22 changed files with 170 additions and 158 deletions
				
			
		|  | @ -6,7 +6,7 @@ import {FromJSON} from "./FromJSON"; | ||||||
| import SharedTagRenderings from "../SharedTagRenderings"; | import SharedTagRenderings from "../SharedTagRenderings"; | ||||||
| import {TagRenderingConfigJson} from "./TagRenderingConfigJson"; | import {TagRenderingConfigJson} from "./TagRenderingConfigJson"; | ||||||
| import {Translation} from "../../UI/i18n/Translation"; | import {Translation} from "../../UI/i18n/Translation"; | ||||||
| import {Img} from "../../UI/Img"; | import Img from "../../UI/Base/Img"; | ||||||
| import Svg from "../../Svg"; | import Svg from "../../Svg"; | ||||||
| import {SubstitutedTranslation} from "../../UI/SpecialVisualizations"; | import {SubstitutedTranslation} from "../../UI/SpecialVisualizations"; | ||||||
| import {Utils} from "../../Utils"; | import {Utils} from "../../Utils"; | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ import State from "./State"; | ||||||
| import {WelcomeMessage} from "./UI/WelcomeMessage"; | import {WelcomeMessage} from "./UI/WelcomeMessage"; | ||||||
| import {LayerSelection} from "./UI/LayerSelection"; | import {LayerSelection} from "./UI/LayerSelection"; | ||||||
| import {VariableUiElement} from "./UI/Base/VariableUIElement"; | import {VariableUiElement} from "./UI/Base/VariableUIElement"; | ||||||
| import {UpdateFromOverpass} from "./Logic/UpdateFromOverpass"; | import UpdateFromOverpass from "./Logic/UpdateFromOverpass"; | ||||||
| import {UIEventSource} from "./Logic/UIEventSource"; | import {UIEventSource} from "./Logic/UIEventSource"; | ||||||
| import {QueryParameters} from "./Logic/Web/QueryParameters"; | import {QueryParameters} from "./Logic/Web/QueryParameters"; | ||||||
| import {PersonalLayersPanel} from "./UI/PersonalLayersPanel"; | import {PersonalLayersPanel} from "./UI/PersonalLayersPanel"; | ||||||
|  | @ -35,10 +35,11 @@ import Link from "./UI/Base/Link"; | ||||||
| import * as personal from "./assets/themes/personalLayout/personalLayout.json" | import * as personal from "./assets/themes/personalLayout/personalLayout.json" | ||||||
| import LayoutConfig from "./Customizations/JSON/LayoutConfig"; | import LayoutConfig from "./Customizations/JSON/LayoutConfig"; | ||||||
| import * as L from "leaflet"; | import * as L from "leaflet"; | ||||||
| import {Img} from "./UI/Img"; | import Img from "./UI/Base/Img"; | ||||||
| import {UserDetails} from "./Logic/Osm/OsmConnection"; | import {UserDetails} from "./Logic/Osm/OsmConnection"; | ||||||
| import Attribution from "./UI/Misc/Attribution"; | import Attribution from "./UI/Misc/Attribution"; | ||||||
| import Constants from "./Models/Constants"; | import Constants from "./Models/Constants"; | ||||||
|  | import MetaTagging from "./Logic/MetaTagging"; | ||||||
| 
 | 
 | ||||||
| export class InitUiElements { | export class InitUiElements { | ||||||
| 
 | 
 | ||||||
|  | @ -340,7 +341,7 @@ export class InitUiElements { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static InitBaseMap() { |     static InitBaseMap() { | ||||||
|          | 
 | ||||||
|         const attr = new Attribution(State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, State.state.leafletMap); |         const attr = new Attribution(State.state.locationControl, State.state.osmConnection.userDetails, State.state.layoutToUse, State.state.leafletMap); | ||||||
|         const bm = new Basemap("leafletDiv", |         const bm = new Basemap("leafletDiv", | ||||||
|             State.state.locationControl, |             State.state.locationControl, | ||||||
|  | @ -349,22 +350,16 @@ export class InitUiElements { | ||||||
|             attr |             attr | ||||||
|         ); |         ); | ||||||
|         State.state.leafletMap.setData(bm.map); |         State.state.leafletMap.setData(bm.map); | ||||||
|          | 
 | ||||||
|         bm.map.on("popupclose", () => { |         bm.map.on("popupclose", () => { | ||||||
|             State.state.selectedElement.setData(undefined) |             State.state.selectedElement.setData(undefined) | ||||||
|         }) |         }) | ||||||
|          |  | ||||||
|          |  | ||||||
|         State.state.layerUpdater = new UpdateFromOverpass(State.state); |  | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static InitLayers() { |     static InitLayers() { | ||||||
| 
 |  | ||||||
|         const flayers: FilteredLayer[] = [] |  | ||||||
| 
 |  | ||||||
|         const state = State.state; |         const state = State.state; | ||||||
| 
 |         const flayers: FilteredLayer[] = [] | ||||||
|         for (const layer of state.layoutToUse.data.layers) { |         for (const layer of state.layoutToUse.data.layers) { | ||||||
| 
 | 
 | ||||||
|             if (typeof (layer) === "string") { |             if (typeof (layer) === "string") { | ||||||
|  | @ -387,6 +382,40 @@ export class InitUiElements { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         State.state.filteredLayers.setData(flayers); |         State.state.filteredLayers.setData(flayers); | ||||||
|  | 
 | ||||||
|  |         const updater = new UpdateFromOverpass(state.locationControl, state.layoutToUse, state.leafletMap); | ||||||
|  |         State.state.layerUpdater = updater; | ||||||
|  | 
 | ||||||
|  |         updater.features.addCallback(features => { | ||||||
|  | 
 | ||||||
|  |             features.forEach(feature => { | ||||||
|  |                 State.state.allElements.addElement(feature); | ||||||
|  |             }) | ||||||
|  |             MetaTagging.addMetatags(features); | ||||||
|  | 
 | ||||||
|  |             function renderLayers(layers) { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |                 if (layers.length === 0) { | ||||||
|  |                     if (features.length > 0) { | ||||||
|  |                         console.warn("Got some leftovers: ", features.join("; ")) | ||||||
|  |                     } | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                 // We use window.setTimeout to give JS some time to update everything and make the interface not too laggy
 | ||||||
|  |                 window.setTimeout(() => { | ||||||
|  |                     const layer = layers[0]; | ||||||
|  |                     const rest = layers.slice(1, layers.length); | ||||||
|  |                     features = layer.SetApplicableData(features); | ||||||
|  |                     renderLayers(rest); | ||||||
|  |                 }, 50) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             renderLayers(flayers); | ||||||
|  | 
 | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static setupAllLayerElements() { |     private static setupAllLayerElements() { | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								Logic/Actors/FeatureSource.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Logic/Actors/FeatureSource.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | import {UIEventSource} from "../UIEventSource"; | ||||||
|  | 
 | ||||||
|  | export default interface FeatureSource { | ||||||
|  |      | ||||||
|  |     features : UIEventSource<any[]>; | ||||||
|  |     freshness: UIEventSource<Date>; | ||||||
|  |      | ||||||
|  | } | ||||||
|  | @ -3,7 +3,7 @@ import {UIEventSource} from "../UIEventSource"; | ||||||
| import {UIElement} from "../../UI/UIElement"; | import {UIElement} from "../../UI/UIElement"; | ||||||
| import {Utils} from "../../Utils"; | import {Utils} from "../../Utils"; | ||||||
| import Svg from "../../Svg"; | import Svg from "../../Svg"; | ||||||
| import {Img} from "../../UI/Img"; | import Img from "../../UI/Base/Img"; | ||||||
| 
 | 
 | ||||||
| export class GeoLocationHandler extends UIElement { | export class GeoLocationHandler extends UIElement { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,12 +1,12 @@ | ||||||
| import LayoutConfig from "../Customizations/JSON/LayoutConfig"; | import {UIEventSource} from "../UIEventSource"; | ||||||
| import {OsmPreferences} from "./Osm/OsmPreferences"; | import LayoutConfig from "../../Customizations/JSON/LayoutConfig"; | ||||||
| import {UIEventSource} from "./UIEventSource"; | import {OsmConnection} from "../Osm/OsmConnection"; | ||||||
| import {OsmConnection} from "./Osm/OsmConnection"; |  | ||||||
| 
 | 
 | ||||||
| export default class InstalledThemes { | export default class InstalledThemes { | ||||||
| 
 |     public installedThemes: UIEventSource<{ layout: LayoutConfig; definition: string }[]>; | ||||||
|     static InstalledThemes(osmConnection: OsmConnection): UIEventSource<{ layout: LayoutConfig; definition: string }[]> { |      | ||||||
|         return osmConnection.preferencesHandler.preferences.map<{ layout: LayoutConfig, definition: string }[]>(allPreferences => { |     constructor(osmConnection: OsmConnection) { | ||||||
|  |         this.installedThemes = osmConnection.preferencesHandler.preferences.map<{ layout: LayoutConfig, definition: string }[]>(allPreferences => { | ||||||
|             const installedThemes: { layout: LayoutConfig, definition: string }[] = []; |             const installedThemes: { layout: LayoutConfig, definition: string }[] = []; | ||||||
|             if (allPreferences === undefined) { |             if (allPreferences === undefined) { | ||||||
|                 console.log("All prefs is undefined"); |                 console.log("All prefs is undefined"); | ||||||
|  | @ -41,7 +41,6 @@ export default class InstalledThemes { | ||||||
|             return installedThemes; |             return installedThemes; | ||||||
| 
 | 
 | ||||||
|         }); |         }); | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static DeleteInvalid(osmConnection: OsmConnection, invalidThemes: any[]) { |     private static DeleteInvalid(osmConnection: OsmConnection, invalidThemes: any[]) { | ||||||
|  | @ -1,9 +1,9 @@ | ||||||
| import * as L from "leaflet"; | import * as L from "leaflet"; | ||||||
| import {UIElement} from "../../UI/UIElement"; | import {UIElement} from "../../UI/UIElement"; | ||||||
| import {Img} from "../../UI/Img"; |  | ||||||
| import Svg from "../../Svg"; | import Svg from "../../Svg"; | ||||||
| import {UIEventSource} from "../UIEventSource"; | import {UIEventSource} from "../UIEventSource"; | ||||||
| import {FilteredLayer} from "../FilteredLayer"; | import {FilteredLayer} from "../FilteredLayer"; | ||||||
|  | import Img from "../../UI/Base/Img"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * The stray-click-hanlders adds a marker to the map if no feature was clicked. |  * The stray-click-hanlders adds a marker to the map if no feature was clicked. | ||||||
|  |  | ||||||
|  | @ -23,8 +23,8 @@ export class FilteredLayer { | ||||||
|     public readonly name: string | UIElement; |     public readonly name: string | UIElement; | ||||||
|     public readonly filters: TagsFilter; |     public readonly filters: TagsFilter; | ||||||
|     public readonly isDisplayed: UIEventSource<boolean> = new UIEventSource(true); |     public readonly isDisplayed: UIEventSource<boolean> = new UIEventSource(true); | ||||||
|     private readonly combinedIsDisplayed: UIEventSource<boolean>; |  | ||||||
|     public readonly layerDef: LayerConfig; |     public readonly layerDef: LayerConfig; | ||||||
|  |     private readonly combinedIsDisplayed: UIEventSource<boolean>; | ||||||
|     private readonly _maxAllowedOverlap: number; |     private readonly _maxAllowedOverlap: number; | ||||||
| 
 | 
 | ||||||
|     /** The featurecollection from overpass |     /** The featurecollection from overpass | ||||||
|  | @ -37,10 +37,10 @@ export class FilteredLayer { | ||||||
|      * The leaflet layer object which should be removed on rerendering |      * The leaflet layer object which should be removed on rerendering | ||||||
|      */ |      */ | ||||||
|     private _geolayer; |     private _geolayer; | ||||||
|      | 
 | ||||||
|     private _showOnPopup: (tags: UIEventSource<any>, feature: any) => UIElement; |     private _showOnPopup: (tags: UIEventSource<any>, feature: any) => UIElement; | ||||||
| 
 | 
 | ||||||
|      | 
 | ||||||
|     constructor( |     constructor( | ||||||
|         layerDef: LayerConfig, |         layerDef: LayerConfig, | ||||||
|         showOnPopup: ((tags: UIEventSource<any>, feature: any) => UIElement) |         showOnPopup: ((tags: UIEventSource<any>, feature: any) => UIElement) | ||||||
|  | @ -68,25 +68,26 @@ export class FilteredLayer { | ||||||
|             } |             } | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * The main function to load data into this layer. |      * The main function to load data into this layer. | ||||||
|      * The data that is NOT used by this layer, is returned as a geojson object; the other data is rendered |      * The data that is NOT used by this layer, is returned as a geojson object; the other data is rendered | ||||||
|      */ |      */ | ||||||
|     public SetApplicableData(geojson: any): any { |     public SetApplicableData(features: any[]): any[] { | ||||||
|         const leftoverFeatures = []; |         const leftoverFeatures = []; | ||||||
|         const selfFeatures = []; |         const selfFeatures = []; | ||||||
|         for (let feature of geojson.features) { |         for (let feature of features) { | ||||||
|             const tags = TagUtils.proprtiesToKV(feature.properties); |             const tags = TagUtils.proprtiesToKV(feature.properties); | ||||||
|             const matches = this.filters.matches(tags); |             const matches = this.filters.matches(tags); | ||||||
|             if (matches) { |             if (matches) { | ||||||
|                selfFeatures.push(feature); |                 selfFeatures.push(feature); | ||||||
|             } |             } | ||||||
|             if (!matches || this.layerDef.passAllFeatures) { |             if (!matches || this.layerDef.passAllFeatures) { | ||||||
|                 leftoverFeatures.push(feature); |                 leftoverFeatures.push(feature); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|        this.RenderLayer(selfFeatures) |         this.RenderLayer(selfFeatures) | ||||||
| 
 | 
 | ||||||
|         const notShadowed = []; |         const notShadowed = []; | ||||||
|         for (const feature of leftoverFeatures) { |         for (const feature of leftoverFeatures) { | ||||||
|  | @ -100,16 +101,13 @@ export class FilteredLayer { | ||||||
|             notShadowed.push(feature); |             notShadowed.push(feature); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return { |         return notShadowed; | ||||||
|             type: "FeatureCollection", |  | ||||||
|             features: notShadowed |  | ||||||
|         }; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     public AddNewElement(element) { |     public AddNewElement(element) { | ||||||
|         this._newElements.push(element); |         this._newElements.push(element); | ||||||
|         this.RenderLayer( this._dataFromOverpass); // Update the layer
 |         this.RenderLayer(this._dataFromOverpass); // Update the layer
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private RenderLayer(features) { |     private RenderLayer(features) { | ||||||
|  | @ -197,7 +195,7 @@ export class FilteredLayer { | ||||||
|                     // We already open it
 |                     // We already open it
 | ||||||
|                     uiElement.Activate(); |                     uiElement.Activate(); | ||||||
|                     popup.setContent(uiElement.Render()); |                     popup.setContent(uiElement.Render()); | ||||||
|                 | 
 | ||||||
|                     const center = GeoOperations.centerpoint(feature).geometry.coordinates; |                     const center = GeoOperations.centerpoint(feature).geometry.coordinates; | ||||||
|                     popup.setLatLng({lat: center[1], lng: center[0]}); |                     popup.setLatLng({lat: center[1], lng: center[0]}); | ||||||
|                     popup.openOn(State.state.leafletMap.data); |                     popup.openOn(State.state.leafletMap.data); | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import opening_hours from "opening_hours"; | ||||||
| import {And, Or, Tag} from "./Tags"; | import {And, Or, Tag} from "./Tags"; | ||||||
| import {Utils} from "../Utils"; | import {Utils} from "../Utils"; | ||||||
| import CountryCoder from "latlon2country" | import CountryCoder from "latlon2country" | ||||||
|  | import {UIEventSource} from "./UIEventSource"; | ||||||
| 
 | 
 | ||||||
| class SimpleMetaTagger { | class SimpleMetaTagger { | ||||||
|     private _f: (feature: any, index: number) => void; |     private _f: (feature: any, index: number) => void; | ||||||
|  | @ -264,7 +265,7 @@ export default class MetaTagging { | ||||||
|         } |         } | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     public static metatags = [ |     private static metatags = [ | ||||||
|         MetaTagging.latlon, |         MetaTagging.latlon, | ||||||
|         MetaTagging.surfaceArea, |         MetaTagging.surfaceArea, | ||||||
|         MetaTagging.country, |         MetaTagging.country, | ||||||
|  | @ -274,6 +275,10 @@ export default class MetaTagging { | ||||||
| 
 | 
 | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * An actor which adds metatags on every feature in the given object | ||||||
|  |      * The features are a list of geojson-features, with a "properties"-field and geometry | ||||||
|  |      */ | ||||||
|     static addMetatags(features: any[]) { |     static addMetatags(features: any[]) { | ||||||
| 
 | 
 | ||||||
|         for (const metatag of MetaTagging.metatags) { |         for (const metatag of MetaTagging.metatags) { | ||||||
|  |  | ||||||
|  | @ -4,9 +4,9 @@ import {UIEventSource} from "../UIEventSource"; | ||||||
| import {OsmPreferences} from "./OsmPreferences"; | import {OsmPreferences} from "./OsmPreferences"; | ||||||
| import {ChangesetHandler} from "./ChangesetHandler"; | import {ChangesetHandler} from "./ChangesetHandler"; | ||||||
| import {ElementStorage} from "../ElementStorage"; | import {ElementStorage} from "../ElementStorage"; | ||||||
| import {Img} from "../../UI/Img"; |  | ||||||
| import Svg from "../../Svg"; | import Svg from "../../Svg"; | ||||||
| import LayoutConfig from "../../Customizations/JSON/LayoutConfig"; | import LayoutConfig from "../../Customizations/JSON/LayoutConfig"; | ||||||
|  | import Img from "../../UI/Base/Img"; | ||||||
| 
 | 
 | ||||||
| export class UserDetails { | export class UserDetails { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import {Bounds} from "../Bounds"; |  | ||||||
| import {TagsFilter} from "../Tags"; | import {TagsFilter} from "../Tags"; | ||||||
| import * as $ from "jquery" | import * as $ from "jquery" | ||||||
| import * as OsmToGeoJson from "osmtogeojson"; | import * as OsmToGeoJson from "osmtogeojson"; | ||||||
|  | import Bounds from "../../Models/Bounds"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Interfaces overpass to get all the latest data |  * Interfaces overpass to get all the latest data | ||||||
|  | @ -26,7 +26,7 @@ export class Overpass { | ||||||
|         return "https://overpass-api.de/api/interpreter?data=" + encodeURIComponent(query) |         return "https://overpass-api.de/api/interpreter?data=" + encodeURIComponent(query) | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     queryGeoJson(bounds: Bounds, continuation: ((any) => void), onFail: ((reason) => void)): void { |     queryGeoJson(bounds: Bounds, continuation: ((any, date: Date) => void), onFail: ((reason) => void)): void { | ||||||
| 
 | 
 | ||||||
|         let query = this.buildQuery("[bbox:" + bounds.south + "," + bounds.west + "," + bounds.north + "," + bounds.east + "]") |         let query = this.buildQuery("[bbox:" + bounds.south + "," + bounds.west + "," + bounds.north + "," + bounds.east + "]") | ||||||
| 
 | 
 | ||||||
|  | @ -48,7 +48,8 @@ export class Overpass { | ||||||
|                 } |                 } | ||||||
|                 // @ts-ignore
 |                 // @ts-ignore
 | ||||||
|                 const geojson = OsmToGeoJson.default(json); |                 const geojson = OsmToGeoJson.default(json); | ||||||
|                 continuation(geojson); |                 const osmTime = new Date(json.osm3s.timestamp_osm_base); | ||||||
|  |                 continuation(geojson, osmTime); | ||||||
|             }).fail(onFail) |             }).fail(onFail) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,16 +1,27 @@ | ||||||
| import {Or, TagsFilter} from "./Tags"; | import {Or, TagsFilter} from "./Tags"; | ||||||
| import {UIEventSource} from "./UIEventSource"; | import {UIEventSource} from "./UIEventSource"; | ||||||
| import {FilteredLayer} from "./FilteredLayer"; | import Bounds from "../Models/Bounds"; | ||||||
| import {Bounds} from "./Bounds"; |  | ||||||
| import {Overpass} from "./Osm/Overpass"; | import {Overpass} from "./Osm/Overpass"; | ||||||
| import State from "../State"; | import Loc from "../Models/Loc"; | ||||||
| import MetaTagging from "./MetaTagging"; | import LayoutConfig from "../Customizations/JSON/LayoutConfig"; | ||||||
|  | import FeatureSource from "./Actors/FeatureSource"; | ||||||
| 
 | 
 | ||||||
| export class UpdateFromOverpass { | export default class UpdateFromOverpass implements FeatureSource{ | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * The last loaded features of the geojson | ||||||
|  |      */ | ||||||
|  |     public readonly features: UIEventSource<any[]> = new UIEventSource<any[]>(undefined); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * The time of updating according to Overpass | ||||||
|  |      */ | ||||||
|  |     public readonly freshness:UIEventSource<Date> = new UIEventSource<Date>(undefined); | ||||||
| 
 | 
 | ||||||
|     public readonly sufficientlyZoomed: UIEventSource<boolean>; |     public readonly sufficientlyZoomed: UIEventSource<boolean>; | ||||||
|     public readonly runningQuery: UIEventSource<boolean> = new UIEventSource<boolean>(false); |     public readonly runningQuery: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||||
|     public readonly retries: UIEventSource<number> = new UIEventSource<number>(0); |     public readonly retries: UIEventSource<number> = new UIEventSource<number>(0); | ||||||
|  |      | ||||||
|     /** |     /** | ||||||
|      * The previous bounds for which the query has been run at the given zoom level |      * The previous bounds for which the query has been run at the given zoom level | ||||||
|      * |      * | ||||||
|  | @ -18,55 +29,58 @@ export class UpdateFromOverpass { | ||||||
|      * If the map location changes, we check for each layer if it is loaded: |      * If the map location changes, we check for each layer if it is loaded: | ||||||
|      * we start checking the bounds at the first zoom level the layer might operate. If in bounds - no reload needed, otherwise we continue walking down |      * we start checking the bounds at the first zoom level the layer might operate. If in bounds - no reload needed, otherwise we continue walking down | ||||||
|      */ |      */ | ||||||
|     private readonly previousBounds: Map<number, Bounds[]> = new Map<number, Bounds[]>(); |     private readonly _previousBounds: Map<number, Bounds[]> = new Map<number, Bounds[]>(); | ||||||
|  |     private readonly _location: UIEventSource<Loc>; | ||||||
|  |     private readonly _layoutToUse: UIEventSource<LayoutConfig>; | ||||||
|  |     private readonly _leafletMap: UIEventSource<L.Map>; | ||||||
| 
 | 
 | ||||||
|     private readonly state: State; |  | ||||||
|      |  | ||||||
|     /** |     /** | ||||||
|      * The most important layer should go first, as that one gets first pick for the questions |      * The most important layer should go first, as that one gets first pick for the questions | ||||||
|      */ |      */ | ||||||
|     constructor(state: State) { |     constructor( | ||||||
|         this.state = state; |         location: UIEventSource<Loc>, | ||||||
|  |         layoutToUse: UIEventSource<LayoutConfig>, | ||||||
|  |         leafletMap: UIEventSource<L.Map>) { | ||||||
|  |         this._location = location; | ||||||
|  |         this._layoutToUse = layoutToUse; | ||||||
|  |         this._leafletMap = leafletMap; | ||||||
|         const self = this; |         const self = this; | ||||||
| 
 | 
 | ||||||
|         this.sufficientlyZoomed = State.state.locationControl.map(location => { |         this.sufficientlyZoomed = location.map(location => { | ||||||
|                 if(location?.zoom === undefined){ |                 if(location?.zoom === undefined){ | ||||||
|                     return false; |                     return false; | ||||||
|                 } |                 } | ||||||
|                 let minzoom = Math.min(...state.layoutToUse.data.layers.map(layer => layer.minzoom ?? 18)); |                 let minzoom = Math.min(...layoutToUse.data.layers.map(layer => layer.minzoom ?? 18)); | ||||||
|                 return location.zoom >= minzoom; |                 return location.zoom >= minzoom; | ||||||
|             }, [state.layoutToUse] |             }, [layoutToUse] | ||||||
|         ); |         ); | ||||||
|         for (let i = 0; i < 25; i++) { |         for (let i = 0; i < 25; i++) { | ||||||
|             // This update removes all data on all layers -> erase the map on lower levels too
 |             // This update removes all data on all layers -> erase the map on lower levels too
 | ||||||
|             this.previousBounds.set(i, []); |             this._previousBounds.set(i, []); | ||||||
|         } |         } | ||||||
|         state.locationControl.addCallback(() => { |         | ||||||
|             self.update(state) |         layoutToUse.addCallback(() => { | ||||||
|  |             self.update() | ||||||
|         }); |         }); | ||||||
|         state.layoutToUse.addCallback(() => { |         location.addCallbackAndRun(() => { | ||||||
|             self.update(state) |             self.update() | ||||||
|         }); |         }); | ||||||
| 
 |  | ||||||
|         self.update(state); |  | ||||||
|          |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public ForceRefresh() { |     public ForceRefresh() { | ||||||
|         for (let i = 0; i < 25; i++) { |         for (let i = 0; i < 25; i++) { | ||||||
|             this.previousBounds.set(i, []); |             this._previousBounds.set(i, []); | ||||||
|         } |         } | ||||||
|         this.update(this.state); |         this.update(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private GetFilter(state: State) { |     private GetFilter() { | ||||||
|         const filters: TagsFilter[] = []; |         const filters: TagsFilter[] = []; | ||||||
|         state = state ?? State.state; |         for (const layer of this._layoutToUse.data.layers) { | ||||||
|         for (const layer of state.layoutToUse.data.layers) { |  | ||||||
|             if(typeof(layer) === "string"){ |             if(typeof(layer) === "string"){ | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|             if (state.locationControl.data.zoom < layer.minzoom) { |             if (this._location.data.zoom < layer.minzoom) { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|             if(layer.doNotDownload){ |             if(layer.doNotDownload){ | ||||||
|  | @ -77,12 +91,12 @@ export class UpdateFromOverpass { | ||||||
|             // Check if data for this layer has already been loaded
 |             // Check if data for this layer has already been loaded
 | ||||||
|             let previouslyLoaded = false; |             let previouslyLoaded = false; | ||||||
|             for (let z = layer.minzoom; z < 25 && !previouslyLoaded; z++) { |             for (let z = layer.minzoom; z < 25 && !previouslyLoaded; z++) { | ||||||
|                 const previousLoadedBounds = this.previousBounds.get(z); |                 const previousLoadedBounds = this._previousBounds.get(z); | ||||||
|                 if (previousLoadedBounds === undefined) { |                 if (previousLoadedBounds === undefined) { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|                 for (const previousLoadedBound of previousLoadedBounds) { |                 for (const previousLoadedBound of previousLoadedBounds) { | ||||||
|                     previouslyLoaded = previouslyLoaded || this.IsInBounds(state, previousLoadedBound); |                     previouslyLoaded = previouslyLoaded || this.IsInBounds(previousLoadedBound); | ||||||
|                     if(previouslyLoaded){ |                     if(previouslyLoaded){ | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|  | @ -98,61 +112,8 @@ export class UpdateFromOverpass { | ||||||
|         } |         } | ||||||
|         return new Or(filters); |         return new Or(filters); | ||||||
|     } |     } | ||||||
|     private handleData(geojson: any) { |     private update(): void { | ||||||
|         const self = this; |         const filter = this.GetFilter(); | ||||||
| 
 |  | ||||||
|         self.retries.setData(0); |  | ||||||
| 
 |  | ||||||
|         let newIds = 1; |  | ||||||
|         for (const feature of geojson.features) { |  | ||||||
|             if (feature.properties.id === undefined) { |  | ||||||
|                 feature.properties.id = "ext/" + newIds; |  | ||||||
|                 newIds++; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         geojson.features.forEach(feature => { |  | ||||||
|             State.state.allElements.addElement(feature); |  | ||||||
|         }) |  | ||||||
|         MetaTagging.addMetatags(geojson.features); |  | ||||||
| 
 |  | ||||||
|         function renderLayers(layers: FilteredLayer[]) { |  | ||||||
|             if (layers.length === 0) { |  | ||||||
|                 self.runningQuery.setData(false); |  | ||||||
| 
 |  | ||||||
|                 if (geojson.features.length > 0) { |  | ||||||
|                     console.warn("Got some leftovers: ", geojson) |  | ||||||
|                 } |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|             // We use window.setTimeout to give JS some time to update everything and make the interface not too laggy
 |  | ||||||
|             window.setTimeout(() => { |  | ||||||
|                 const layer = layers[0]; |  | ||||||
|                 const rest = layers.slice(1, layers.length); |  | ||||||
|                 geojson = layer.SetApplicableData(geojson); |  | ||||||
|                 renderLayers(rest); |  | ||||||
|             }, 50) |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         renderLayers(State.state.filteredLayers.data); |  | ||||||
|     } |  | ||||||
|     private handleFail(state: State, reason: any) { |  | ||||||
|         this.retries.data++; |  | ||||||
|         this.ForceRefresh(); |  | ||||||
|         console.log(`QUERY FAILED (retrying in ${5 * this.retries.data} sec)`, reason); |  | ||||||
|         this.retries.ping(); |  | ||||||
|         this.runningQuery.setData(false) |  | ||||||
|         const self = this; |  | ||||||
|         window?.setTimeout( |  | ||||||
|             function () { |  | ||||||
|                 self.update(state) |  | ||||||
|             }, this.retries.data * 5000 |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
|     private update(state: State): void { |  | ||||||
|         const filter = this.GetFilter(state); |  | ||||||
|         if (filter === undefined) { |         if (filter === undefined) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  | @ -162,9 +123,9 @@ export class UpdateFromOverpass { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const bounds = state.leafletMap.data.getBounds(); |         const bounds = this._leafletMap.data.getBounds(); | ||||||
| 
 | 
 | ||||||
|         const diff = state.layoutToUse.data.widenFactor; |         const diff = this._layoutToUse.data.widenFactor; | ||||||
| 
 | 
 | ||||||
|         const n = Math.min(90, bounds.getNorth() + diff); |         const n = Math.min(90, bounds.getNorth() + diff); | ||||||
|         const e = Math.min(180, bounds.getEast() + diff); |         const e = Math.min(180, bounds.getEast() + diff); | ||||||
|  | @ -172,18 +133,30 @@ export class UpdateFromOverpass { | ||||||
|         const w = Math.max(-180, bounds.getWest() - diff); |         const w = Math.max(-180, bounds.getWest() - diff); | ||||||
|         const queryBounds = {north: n, east: e, south: s, west: w}; |         const queryBounds = {north: n, east: e, south: s, west: w}; | ||||||
| 
 | 
 | ||||||
|         const z = Math.floor(state.locationControl.data.zoom); |         const z = Math.floor(this._location.data.zoom); | ||||||
| 
 | 
 | ||||||
|         this.runningQuery.setData(true); |         this.runningQuery.setData(true); | ||||||
|         const self = this; |         const self = this; | ||||||
|         const overpass = new Overpass(filter); |         const overpass = new Overpass(filter); | ||||||
|         overpass.queryGeoJson(queryBounds, |         overpass.queryGeoJson(queryBounds, | ||||||
|             function (data) { |             function (data, date) { | ||||||
|                 self.previousBounds.get(z).push(queryBounds); |                 self._previousBounds.get(z).push(queryBounds); | ||||||
|                 self.handleData(data) |                 self.retries.setData(0); | ||||||
|  |                 self.freshness.setData(date); | ||||||
|  |                 self.features.setData(data.features); | ||||||
|  |                 self.runningQuery.setData(false); | ||||||
|             }, |             }, | ||||||
|             function (reason) { |             function (reason) { | ||||||
|                 self.handleFail(state, reason) |                 self.retries.data++; | ||||||
|  |                 self.ForceRefresh(); | ||||||
|  |                 console.log(`QUERY FAILED (retrying in ${5 * self.retries.data} sec)`, undefined); | ||||||
|  |                 self.retries.ping(); | ||||||
|  |                 self.runningQuery.setData(false) | ||||||
|  |                 window?.setTimeout( | ||||||
|  |                     function () { | ||||||
|  |                         self.update() | ||||||
|  |                     }, self.retries.data * 5000 | ||||||
|  |                 ) | ||||||
|             } |             } | ||||||
|         ); |         ); | ||||||
|          |          | ||||||
|  | @ -191,12 +164,12 @@ export class UpdateFromOverpass { | ||||||
|          |          | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|     private IsInBounds(state: State, bounds: Bounds): boolean { |     private IsInBounds(bounds: Bounds): boolean { | ||||||
|         if (this.previousBounds === undefined) { |         if (this._previousBounds === undefined) { | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const b = state.leafletMap.data.getBounds(); |         const b = this._leafletMap.data.getBounds(); | ||||||
|         return b.getSouth() >= bounds.south && |         return b.getSouth() >= bounds.south && | ||||||
|             b.getNorth() <= bounds.north && |             b.getNorth() <= bounds.north && | ||||||
|             b.getEast() <= bounds.east && |             b.getEast() <= bounds.east && | ||||||
|  |  | ||||||
							
								
								
									
										6
									
								
								Models/Bounds.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								Models/Bounds.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | ||||||
|  | export default interface Bounds { | ||||||
|  |     north: number, | ||||||
|  |     east: number, | ||||||
|  |     south: number, | ||||||
|  |     west: number | ||||||
|  | } | ||||||
							
								
								
									
										4
									
								
								State.ts
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								State.ts
									
										
									
									
									
								
							|  | @ -13,7 +13,7 @@ import {QueryParameters} from "./Logic/Web/QueryParameters"; | ||||||
| import LayoutConfig from "./Customizations/JSON/LayoutConfig"; | import LayoutConfig from "./Customizations/JSON/LayoutConfig"; | ||||||
| import Hash from "./Logic/Web/Hash"; | import Hash from "./Logic/Web/Hash"; | ||||||
| import {MangroveIdentity} from "./Logic/Web/MangroveReviews"; | import {MangroveIdentity} from "./Logic/Web/MangroveReviews"; | ||||||
| import InstalledThemes from "./Logic/InstalledThemes"; | import InstalledThemes from "./Logic/Actors/InstalledThemes"; | ||||||
| import {BaseLayer} from "./Models/BaseLayer"; | import {BaseLayer} from "./Models/BaseLayer"; | ||||||
| import Loc from "./Models/Loc"; | import Loc from "./Models/Loc"; | ||||||
| import Constants from "./Models/Constants"; | import Constants from "./Models/Constants"; | ||||||
|  | @ -237,7 +237,7 @@ export default class State { | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         this.installedThemes = InstalledThemes.InstalledThemes(this.osmConnection ); |         this.installedThemes = new InstalledThemes(this.osmConnection).installedThemes; | ||||||
| 
 | 
 | ||||||
|         // Important: the favourite layers are initialized _after_ the installed themes, as these might contain an installedTheme
 |         // Important: the favourite layers are initialized _after_ the installed themes, as these might contain an installedTheme
 | ||||||
|         this.favouriteLayers = this.osmConnection.GetLongPreference("favouriteLayers").map( |         this.favouriteLayers = this.osmConnection.GetLongPreference("favouriteLayers").map( | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								Svg.ts
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								Svg.ts
									
										
									
									
									
								
							|  | @ -1,4 +1,4 @@ | ||||||
| import {Img} from "./UI/Img"; | import Img from "./UI/Base/Img"; | ||||||
| import {FixedUiElement} from "./UI/Base/FixedUiElement"; | import {FixedUiElement} from "./UI/Base/FixedUiElement"; | ||||||
| 
 | 
 | ||||||
| export default class Svg { | export default class Svg { | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| export class Img { | export default class Img { | ||||||
| 
 | 
 | ||||||
|     public static runningFromConsole = false; |     public static runningFromConsole = false; | ||||||
|      |      | ||||||
|  | @ -1,10 +1,7 @@ | ||||||
| import {UIElement} from "../UIElement"; | import {UIElement} from "../UIElement"; | ||||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | import {UIEventSource} from "../../Logic/UIEventSource"; | ||||||
| import {LicenseInfo} from "../../Logic/Web/Wikimedia"; | import {LicenseInfo} from "../../Logic/Web/Wikimedia"; | ||||||
| import {Imgur} from "../../Logic/Web/Imgur"; |  | ||||||
| import {Mapillary} from "../../Logic/Web/Mapillary"; | import {Mapillary} from "../../Logic/Web/Mapillary"; | ||||||
| import {Img} from "../Img"; |  | ||||||
| import {FixedUiElement} from "../Base/FixedUiElement"; |  | ||||||
| import Svg from "../../Svg"; | import Svg from "../../Svg"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -56,15 +56,13 @@ export class MoreScreen extends UIElement { | ||||||
|         if (description !== undefined) { |         if (description !== undefined) { | ||||||
|             description = new Combine(["<br/>", description]); |             description = new Combine(["<br/>", description]); | ||||||
|         } |         } | ||||||
|         const link = |         return new SubtleButton(layout.icon, | ||||||
|             new SubtleButton(layout.icon, |             new Combine([ | ||||||
|                 new Combine([ |                 "<b>", | ||||||
|                     "<b>", |                 Translations.W(layout.title), | ||||||
|                     Translations.W(layout.title), |                 "</b>", | ||||||
|                     "</b>", |                 description ?? "", | ||||||
|                     description ?? "", |             ]), {url: linkText, newTab: false}); | ||||||
|                 ]), {url: linkText, newTab: false}) |  | ||||||
|         return link; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     InnerRender(): string { |     InnerRender(): string { | ||||||
|  |  | ||||||
|  | @ -57,7 +57,7 @@ export class SearchAndGo extends UIElement { | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 const bb = result[0].boundingbox; |                 const bb = result[0].boundingbox; | ||||||
|                 const bounds = [ |                 const bounds : [[number, number], [number, number]] = [ | ||||||
|                     [bb[0], bb[2]], |                     [bb[0], bb[2]], | ||||||
|                     [bb[1], bb[3]] |                     [bb[1], bb[3]] | ||||||
|                 ] |                 ] | ||||||
|  |  | ||||||
|  | @ -6,7 +6,6 @@ import {VariableUiElement} from "./Base/VariableUIElement"; | ||||||
| import CheckBox from "./Input/CheckBox"; | import CheckBox from "./Input/CheckBox"; | ||||||
| import {VerticalCombine} from "./Base/VerticalCombine"; | import {VerticalCombine} from "./Base/VerticalCombine"; | ||||||
| import State from "../State"; | import State from "../State"; | ||||||
| import {Basemap} from "../Logic/Leaflet/Basemap"; |  | ||||||
| import {FilteredLayer} from "../Logic/FilteredLayer"; | import {FilteredLayer} from "../Logic/FilteredLayer"; | ||||||
| import {Utils} from "../Utils"; | import {Utils} from "../Utils"; | ||||||
| import {UIEventSource} from "../Logic/UIEventSource"; | import {UIEventSource} from "../Logic/UIEventSource"; | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| import {Img} from "../UI/Img" | import Img from "../UI/Base/Img" | ||||||
| import {UIElement} from "../UI/UIElement"; | import {UIElement} from "../UI/UIElement"; | ||||||
| Img.runningFromConsole = true; | Img.runningFromConsole = true; | ||||||
| // We HAVE to mark this while importing
 | // We HAVE to mark this while importing
 | ||||||
|  |  | ||||||
|  | @ -1,12 +1,11 @@ | ||||||
| import * as fs from "fs"; | import * as fs from "fs"; | ||||||
| import {Utils} from "../Utils"; |  | ||||||
| 
 | 
 | ||||||
| function genImages() { | function genImages() { | ||||||
| 
 | 
 | ||||||
|     console.log("Generating images") |     console.log("Generating images") | ||||||
|     const dir = fs.readdirSync("./assets/svg") |     const dir = fs.readdirSync("./assets/svg") | ||||||
| 
 | 
 | ||||||
|     let module = "import {Img} from \"./UI/Img\";\nimport {FixedUiElement} from \"./UI/Base/FixedUiElement\";\n\nexport default class Svg {\n\n\n"; |     let module = "import Img from \"./UI/Base/Img\";\nimport {FixedUiElement} from \"./UI/Base/FixedUiElement\";\n\nexport default class Svg {\n\n\n"; | ||||||
|     const allNames: string[] = []; |     const allNames: string[] = []; | ||||||
|     for (const path of dir) { |     for (const path of dir) { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import {UIElement} from "../UI/UIElement"; | import {UIElement} from "../UI/UIElement"; | ||||||
| UIElement.runningFromConsole = true; | UIElement.runningFromConsole = true; | ||||||
| import {Img} from "../UI/Img"; | import Img from "../UI/Base/Img"; | ||||||
| Img.runningFromConsole = true; | Img.runningFromConsole = true; | ||||||
| import {equal} from "assert"; | import {equal} from "assert"; | ||||||
| import T from "./TestHelper"; | import T from "./TestHelper"; | ||||||
|  | @ -9,13 +9,13 @@ import {And, Tag} from "../Logic/Tags"; | ||||||
| import Locale from "../UI/i18n/Locale"; | import Locale from "../UI/i18n/Locale"; | ||||||
| import Translations from "../UI/i18n/Translations"; | import Translations from "../UI/i18n/Translations"; | ||||||
| import {UIEventSource} from "../Logic/UIEventSource"; | import {UIEventSource} from "../Logic/UIEventSource"; | ||||||
| import {OH, OpeningHour} from "../Logic/OpeningHours"; |  | ||||||
| import PublicHolidayInput from "../UI/Input/OpeningHours/PublicHolidayInput"; |  | ||||||
| import TagRenderingConfig from "../Customizations/JSON/TagRenderingConfig"; | import TagRenderingConfig from "../Customizations/JSON/TagRenderingConfig"; | ||||||
| import EditableTagRendering from "../UI/Popup/EditableTagRendering"; | import EditableTagRendering from "../UI/Popup/EditableTagRendering"; | ||||||
| import {SubstitutedTranslation} from "../UI/SpecialVisualizations"; | import {SubstitutedTranslation} from "../UI/SpecialVisualizations"; | ||||||
| import {Utils} from "../Utils"; | import {Utils} from "../Utils"; | ||||||
| import {Translation} from "../UI/i18n/Translation"; | import {Translation} from "../UI/i18n/Translation"; | ||||||
|  | import {OH, OpeningHour} from "../UI/OpeningHours/OpeningHours"; | ||||||
|  | import PublicHolidayInput from "../UI/OpeningHours/PublicHolidayInput"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| new T([ | new T([ | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue