| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; | 
					
						
							|  |  |  | import FilteringFeatureSource from "./Sources/FilteringFeatureSource"; | 
					
						
							|  |  |  | import PerLayerFeatureSourceSplitter from "./PerLayerFeatureSourceSplitter"; | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  | import FeatureSource, {FeatureSourceForLayer, FeatureSourceState, IndexedFeatureSource, Tiled} from "./FeatureSource"; | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | import TiledFeatureSource from "./TiledFeatureSource/TiledFeatureSource"; | 
					
						
							| 
									
										
										
										
											2021-01-15 00:29:07 +01:00
										 |  |  | import {UIEventSource} from "../UIEventSource"; | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | import {TileHierarchyTools} from "./TiledFeatureSource/TileHierarchy"; | 
					
						
							| 
									
										
										
										
											2021-07-27 19:39:57 +02:00
										 |  |  | import FilteredLayer from "../../Models/FilteredLayer"; | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | import MetaTagging from "../MetaTagging"; | 
					
						
							|  |  |  | import RememberingSource from "./Sources/RememberingSource"; | 
					
						
							|  |  |  | import OverpassFeatureSource from "../Actors/OverpassFeatureSource"; | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  | import {Changes} from "../Osm/Changes"; | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | import GeoJsonSource from "./Sources/GeoJsonSource"; | 
					
						
							|  |  |  | import Loc from "../../Models/Loc"; | 
					
						
							|  |  |  | import WayHandlingApplyingFeatureSource from "./Sources/WayHandlingApplyingFeatureSource"; | 
					
						
							|  |  |  | import RegisteringAllFromFeatureSourceActor from "./Actors/RegisteringAllFromFeatureSourceActor"; | 
					
						
							|  |  |  | import TiledFromLocalStorageSource from "./TiledFeatureSource/TiledFromLocalStorageSource"; | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  | import SaveTileToLocalStorageActor from "./Actors/SaveTileToLocalStorageActor"; | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | import DynamicGeoJsonTileSource from "./TiledFeatureSource/DynamicGeoJsonTileSource"; | 
					
						
							|  |  |  | import {BBox} from "../GeoOperations"; | 
					
						
							|  |  |  | import {TileHierarchyMerger} from "./TiledFeatureSource/TileHierarchyMerger"; | 
					
						
							|  |  |  | import RelationsTracker from "../Osm/RelationsTracker"; | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  | import {NewGeometryFromChangesFeatureSource} from "./Sources/NewGeometryFromChangesFeatureSource"; | 
					
						
							| 
									
										
										
										
											2021-09-22 16:07:56 +02:00
										 |  |  | import ChangeGeometryApplicator from "./Sources/ChangeGeometryApplicator"; | 
					
						
							| 
									
										
										
										
											2021-01-15 00:29:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | export default class FeaturePipeline implements FeatureSourceState { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public readonly sufficientlyZoomed: UIEventSource<boolean>; | 
					
						
							|  |  |  |     public readonly runningQuery: UIEventSource<boolean>; | 
					
						
							|  |  |  |     public readonly timeout: UIEventSource<number>; | 
					
						
							|  |  |  |     public readonly somethingLoaded: UIEventSource<boolean> = new UIEventSource<boolean>(false) | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |     public readonly newDataLoadedSignal: UIEventSource<FeatureSource> = new UIEventSource<FeatureSource>(undefined) | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     private readonly overpassUpdater: OverpassFeatureSource | 
					
						
							|  |  |  |     private readonly relationTracker: RelationsTracker | 
					
						
							|  |  |  |     private readonly perLayerHierarchy: Map<string, TileHierarchyMerger>; | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |     constructor( | 
					
						
							|  |  |  |         handleFeatureSource: (source: FeatureSourceForLayer) => void, | 
					
						
							|  |  |  |         state: { | 
					
						
							|  |  |  |             filteredLayers: UIEventSource<FilteredLayer[]>, | 
					
						
							|  |  |  |             locationControl: UIEventSource<Loc>, | 
					
						
							|  |  |  |             selectedElement: UIEventSource<any>, | 
					
						
							|  |  |  |             changes: Changes, | 
					
						
							|  |  |  |             layoutToUse: UIEventSource<LayoutConfig>, | 
					
						
							|  |  |  |             leafletMap: any, | 
					
						
							|  |  |  |             readonly overpassUrl: UIEventSource<string>; | 
					
						
							|  |  |  |             readonly overpassTimeout: UIEventSource<number>; | 
					
						
							|  |  |  |             readonly overpassMaxZoom: UIEventSource<number>; | 
					
						
							|  |  |  |         }) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const self = this | 
					
						
							|  |  |  |         const updater = new OverpassFeatureSource(state); | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |         updater.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(updater)) | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |         this.overpassUpdater = updater; | 
					
						
							|  |  |  |         this.sufficientlyZoomed = updater.sufficientlyZoomed | 
					
						
							|  |  |  |         this.runningQuery = updater.runningQuery | 
					
						
							|  |  |  |         this.timeout = updater.timeout | 
					
						
							|  |  |  |         this.relationTracker = updater.relationsTracker | 
					
						
							|  |  |  |         // Register everything in the state' 'AllElements'
 | 
					
						
							|  |  |  |         new RegisteringAllFromFeatureSourceActor(updater) | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |          | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const perLayerHierarchy = new Map<string, TileHierarchyMerger>() | 
					
						
							|  |  |  |         this.perLayerHierarchy = perLayerHierarchy | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |         const patchedHandleFeatureSource = function (src: FeatureSourceForLayer & IndexedFeatureSource) { | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |             // This will already contain the merged features for this tile. In other words, this will only be triggered once for every tile
 | 
					
						
							|  |  |  |             const srcFiltered = | 
					
						
							|  |  |  |                 new FilteringFeatureSource(state, | 
					
						
							|  |  |  |                     new WayHandlingApplyingFeatureSource( | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                         new ChangeGeometryApplicator(src, state.changes) | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |                     ) | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             handleFeatureSource(srcFiltered) | 
					
						
							|  |  |  |             self.somethingLoaded.setData(true) | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         function addToHierarchy(src: FeatureSource & Tiled, layerId: string) { | 
					
						
							|  |  |  |             perLayerHierarchy.get(layerId).registerTile(src) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (const filteredLayer of state.filteredLayers.data) { | 
					
						
							|  |  |  |             const hierarchy = new TileHierarchyMerger(filteredLayer, (tile, _) => patchedHandleFeatureSource(tile)) | 
					
						
							|  |  |  |             const id = filteredLayer.layerDef.id | 
					
						
							|  |  |  |             perLayerHierarchy.set(id, hierarchy) | 
					
						
							|  |  |  |             const source = filteredLayer.layerDef.source | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (source.geojsonSource === undefined) { | 
					
						
							|  |  |  |                 // This is an OSM layer
 | 
					
						
							|  |  |  |                 // We load the cached values and register them
 | 
					
						
							|  |  |  |                 // Getting data from upstream happens a bit lower
 | 
					
						
							|  |  |  |                 new TiledFromLocalStorageSource(filteredLayer, | 
					
						
							|  |  |  |                     (src) => { | 
					
						
							|  |  |  |                         new RegisteringAllFromFeatureSourceActor(src) | 
					
						
							|  |  |  |                         hierarchy.registerTile(src); | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                         src.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(src)) | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |                     }, state) | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (source.geojsonZoomLevel === undefined) { | 
					
						
							|  |  |  |                 // This is a 'load everything at once' geojson layer
 | 
					
						
							|  |  |  |                 // We split them up into tiles
 | 
					
						
							|  |  |  |                 const src = new GeoJsonSource(filteredLayer) | 
					
						
							|  |  |  |                 TiledFeatureSource.createHierarchy(src, { | 
					
						
							|  |  |  |                     layer: src.layer, | 
					
						
							|  |  |  |                     registerTile: (tile) => { | 
					
						
							|  |  |  |                         new RegisteringAllFromFeatureSourceActor(tile) | 
					
						
							|  |  |  |                         addToHierarchy(tile, id) | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                         tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |                     } | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 new DynamicGeoJsonTileSource( | 
					
						
							|  |  |  |                     filteredLayer, | 
					
						
							|  |  |  |                     src => TiledFeatureSource.createHierarchy(src, { | 
					
						
							|  |  |  |                         layer: src.layer, | 
					
						
							|  |  |  |                         registerTile: (tile) => { | 
					
						
							|  |  |  |                             new RegisteringAllFromFeatureSourceActor(tile) | 
					
						
							|  |  |  |                             addToHierarchy(tile, id) | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                             tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) | 
					
						
							| 
									
										
										
										
											2021-07-18 21:37:14 +02:00
										 |  |  |                         } | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |                     }), | 
					
						
							|  |  |  |                     state | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Actually load data from the overpass source
 | 
					
						
							|  |  |  |         new PerLayerFeatureSourceSplitter(state.filteredLayers, | 
					
						
							|  |  |  |             (source) => TiledFeatureSource.createHierarchy(source, { | 
					
						
							|  |  |  |                 layer: source.layer, | 
					
						
							|  |  |  |                 registerTile: (tile) => { | 
					
						
							|  |  |  |                     // We save the tile data for the given layer to local storage
 | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                     new SaveTileToLocalStorageActor(tile, tile.tileIndex) | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |                     addToHierarchy(tile, source.layer.layerDef.id); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |             }), | 
					
						
							|  |  |  |             new RememberingSource(updater)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Also load points/lines that are newly added. 
 | 
					
						
							|  |  |  |         const newGeometry = new NewGeometryFromChangesFeatureSource(state.changes) | 
					
						
							|  |  |  |         new RegisteringAllFromFeatureSourceActor(newGeometry) | 
					
						
							|  |  |  |         // A NewGeometryFromChangesFeatureSource does not split per layer, so we do this next
 | 
					
						
							|  |  |  |         new PerLayerFeatureSourceSplitter(state.filteredLayers, | 
					
						
							|  |  |  |             (perLayer) => { | 
					
						
							|  |  |  |                 // We don't bother to split them over tiles as it'll contain little features by default, so we simply add them like this
 | 
					
						
							|  |  |  |                 addToHierarchy(perLayer, perLayer.layer.layerDef.id) | 
					
						
							|  |  |  |                 // AT last, we always apply the metatags whenever possible
 | 
					
						
							|  |  |  |                 perLayer.features.addCallbackAndRunD(_ => self.applyMetaTags(perLayer)) | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             newGeometry | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Whenever fresh data comes in, we need to update the metatagging
 | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |         self.newDataLoadedSignal.stabilized(1000).addCallback(src => { | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |             self.updateAllMetaTagging() | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-15 00:29:07 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |      | 
					
						
							|  |  |  |     private applyMetaTags(src: FeatureSourceForLayer){ | 
					
						
							|  |  |  |         const self = this | 
					
						
							|  |  |  |         MetaTagging.addMetatags( | 
					
						
							|  |  |  |             src.features.data, | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 memberships: this.relationTracker, | 
					
						
							|  |  |  |                 getFeaturesWithin: (layerId, bbox: BBox) => self.GetFeaturesWithin(layerId, bbox) | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             src.layer.layerDef, | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 includeDates: true, | 
					
						
							|  |  |  |                 // We assume that the non-dated metatags are already set by the cache generator
 | 
					
						
							|  |  |  |                 includeNonDates: !src.layer.layerDef.source.isOsmCacheLayer | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-01-15 00:29:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |     private updateAllMetaTagging() { | 
					
						
							|  |  |  |         const self = this; | 
					
						
							|  |  |  |         this.perLayerHierarchy.forEach(hierarchy => { | 
					
						
							|  |  |  |             hierarchy.loadedTiles.forEach(src => { | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |                 self.applyMetaTags(src) | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |             }) | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     public GetAllFeaturesWithin(bbox: BBox): any[][] { | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |         const self = this | 
					
						
							|  |  |  |         const tiles = [] | 
					
						
							|  |  |  |         Array.from(this.perLayerHierarchy.keys()) | 
					
						
							|  |  |  |             .forEach(key => tiles.push(...self.GetFeaturesWithin(key, bbox))) | 
					
						
							|  |  |  |         return tiles; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     public GetFeaturesWithin(layerId: string, bbox: BBox): any[][] { | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |         const requestedHierarchy = this.perLayerHierarchy.get(layerId) | 
					
						
							|  |  |  |         if (requestedHierarchy === undefined) { | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |             console.warn("Layer ", layerId, "is not defined. Try one of ", Array.from(this.perLayerHierarchy.keys())) | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |             return undefined; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return TileHierarchyTools.getTiles(requestedHierarchy, bbox) | 
					
						
							|  |  |  |             .filter(featureSource => featureSource.features?.data !== undefined) | 
					
						
							|  |  |  |             .map(featureSource => featureSource.features.data.map(fs => fs.feature)) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     public GetTilesPerLayerWithin(bbox: BBox, handleTile: (tile: FeatureSourceForLayer & Tiled) => void) { | 
					
						
							|  |  |  |         Array.from(this.perLayerHierarchy.values()).forEach(hierarchy => { | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |             TileHierarchyTools.getTiles(hierarchy, bbox).forEach(handleTile) | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public ForceRefresh() { | 
					
						
							|  |  |  |         this.overpassUpdater.ForceRefresh() | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-01-15 00:29:07 +01:00
										 |  |  | } |