forked from MapComplete/MapComplete
		
	More work on splitting roads, WIP; refactoring tests
This commit is contained in:
		
							parent
							
								
									e374bb355c
								
							
						
					
					
						commit
						1f93923820
					
				
					 62 changed files with 1163 additions and 823 deletions
				
			
		|  | @ -1,7 +1,7 @@ | |||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; | ||||
| import FilteringFeatureSource from "./Sources/FilteringFeatureSource"; | ||||
| import PerLayerFeatureSourceSplitter from "./PerLayerFeatureSourceSplitter"; | ||||
| import FeatureSource, {FeatureSourceForLayer, FeatureSourceState, Tiled} from "./FeatureSource"; | ||||
| import FeatureSource, {FeatureSourceForLayer, FeatureSourceState, IndexedFeatureSource, Tiled} from "./FeatureSource"; | ||||
| import TiledFeatureSource from "./TiledFeatureSource/TiledFeatureSource"; | ||||
| import {UIEventSource} from "../UIEventSource"; | ||||
| import {TileHierarchyTools} from "./TiledFeatureSource/TileHierarchy"; | ||||
|  | @ -14,13 +14,14 @@ import GeoJsonSource from "./Sources/GeoJsonSource"; | |||
| import Loc from "../../Models/Loc"; | ||||
| import WayHandlingApplyingFeatureSource from "./Sources/WayHandlingApplyingFeatureSource"; | ||||
| import RegisteringAllFromFeatureSourceActor from "./Actors/RegisteringAllFromFeatureSourceActor"; | ||||
| import {Utils} from "../../Utils"; | ||||
| import TiledFromLocalStorageSource from "./TiledFeatureSource/TiledFromLocalStorageSource"; | ||||
| import LocalStorageSaverActor from "./Actors/LocalStorageSaverActor"; | ||||
| import SaveTileToLocalStorageActor from "./Actors/SaveTileToLocalStorageActor"; | ||||
| import DynamicGeoJsonTileSource from "./TiledFeatureSource/DynamicGeoJsonTileSource"; | ||||
| import {BBox} from "../GeoOperations"; | ||||
| import {TileHierarchyMerger} from "./TiledFeatureSource/TileHierarchyMerger"; | ||||
| import RelationsTracker from "../Osm/RelationsTracker"; | ||||
| import {NewGeometryFromChangesFeatureSource} from "./Sources/NewGeometryFromChangesFeatureSource"; | ||||
| import ChangeGeometryApplicator from "./ChangeApplicator"; | ||||
| 
 | ||||
| 
 | ||||
| export default class FeaturePipeline implements FeatureSourceState { | ||||
|  | @ -29,10 +30,12 @@ export default class FeaturePipeline implements FeatureSourceState { | |||
|     public readonly runningQuery: UIEventSource<boolean>; | ||||
|     public readonly timeout: UIEventSource<number>; | ||||
|     public readonly somethingLoaded: UIEventSource<boolean> = new UIEventSource<boolean>(false) | ||||
|     public readonly newDataLoadedSignal: UIEventSource<FeatureSource> = new UIEventSource<FeatureSource>(undefined) | ||||
| 
 | ||||
|     private readonly overpassUpdater: OverpassFeatureSource | ||||
|     private readonly relationTracker: RelationsTracker | ||||
|     private readonly perLayerHierarchy: Map<string, TileHierarchyMerger>; | ||||
| 
 | ||||
|     constructor( | ||||
|         handleFeatureSource: (source: FeatureSourceForLayer) => void, | ||||
|         state: { | ||||
|  | @ -49,6 +52,7 @@ export default class FeaturePipeline implements FeatureSourceState { | |||
| 
 | ||||
|         const self = this | ||||
|         const updater = new OverpassFeatureSource(state); | ||||
|         updater.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(updater)) | ||||
|         this.overpassUpdater = updater; | ||||
|         this.sufficientlyZoomed = updater.sufficientlyZoomed | ||||
|         this.runningQuery = updater.runningQuery | ||||
|  | @ -56,16 +60,17 @@ export default class FeaturePipeline implements FeatureSourceState { | |||
|         this.relationTracker = updater.relationsTracker | ||||
|         // Register everything in the state' 'AllElements'
 | ||||
|         new RegisteringAllFromFeatureSourceActor(updater) | ||||
|          | ||||
| 
 | ||||
|         const perLayerHierarchy = new Map<string, TileHierarchyMerger>() | ||||
|         this.perLayerHierarchy = perLayerHierarchy | ||||
| 
 | ||||
|         const patchedHandleFeatureSource = function (src: FeatureSourceForLayer) { | ||||
|         const patchedHandleFeatureSource = function (src: FeatureSourceForLayer & IndexedFeatureSource) { | ||||
|             // 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( | ||||
|                         src, | ||||
|                         new ChangeGeometryApplicator(src, state.changes) | ||||
|                     ) | ||||
|                 ) | ||||
|             handleFeatureSource(srcFiltered) | ||||
|  | @ -90,6 +95,7 @@ export default class FeaturePipeline implements FeatureSourceState { | |||
|                     (src) => { | ||||
|                         new RegisteringAllFromFeatureSourceActor(src) | ||||
|                         hierarchy.registerTile(src); | ||||
|                         src.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(src)) | ||||
|                     }, state) | ||||
|                 continue | ||||
|             } | ||||
|  | @ -103,6 +109,7 @@ export default class FeaturePipeline implements FeatureSourceState { | |||
|                     registerTile: (tile) => { | ||||
|                         new RegisteringAllFromFeatureSourceActor(tile) | ||||
|                         addToHierarchy(tile, id) | ||||
|                         tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) | ||||
|                     } | ||||
|                 }) | ||||
|             } else { | ||||
|  | @ -113,6 +120,7 @@ export default class FeaturePipeline implements FeatureSourceState { | |||
|                         registerTile: (tile) => { | ||||
|                             new RegisteringAllFromFeatureSourceActor(tile) | ||||
|                             addToHierarchy(tile, id) | ||||
|                             tile.features.addCallbackAndRunD(_ => self.newDataLoadedSignal.setData(tile)) | ||||
|                         } | ||||
|                     }), | ||||
|                     state | ||||
|  | @ -122,64 +130,90 @@ export default class FeaturePipeline implements FeatureSourceState { | |||
|         } | ||||
| 
 | ||||
|         // 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
 | ||||
|                     const [z, x, y] = Utils.tile_from_index(tile.tileIndex) | ||||
|                     new LocalStorageSaverActor(tile, x, y, z) | ||||
|                     new SaveTileToLocalStorageActor(tile, tile.tileIndex) | ||||
|                     addToHierarchy(tile, source.layer.layerDef.id); | ||||
|                 } | ||||
|             }), new RememberingSource(updater)) | ||||
|             }), | ||||
|             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 | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
|         // Whenever fresh data comes in, we need to update the metatagging
 | ||||
|         updater.features.addCallback(_ => { | ||||
|         self.newDataLoadedSignal.stabilized(1000).addCallback(src => { | ||||
|             console.log("Got an update from ", src.name) | ||||
|             self.updateAllMetaTagging() | ||||
|         }) | ||||
| 
 | ||||
|     } | ||||
|      | ||||
|     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 | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     private updateAllMetaTagging() { | ||||
|         console.log("Updating the meta tagging") | ||||
|         const self = this; | ||||
|         this.perLayerHierarchy.forEach(hierarchy => { | ||||
|             hierarchy.loadedTiles.forEach(src => { | ||||
|                 MetaTagging.addMetatags( | ||||
|                     src.features.data, | ||||
|                     { | ||||
|                         memberships: this.relationTracker, | ||||
|                         getFeaturesWithin: (layerId, bbox: BBox) => self.GetFeaturesWithin(layerId, bbox) | ||||
|                     }, | ||||
|                     src.layer.layerDef | ||||
|                 ) | ||||
|                 self.applyMetaTags(src) | ||||
|             }) | ||||
|         }) | ||||
| 
 | ||||
|     } | ||||
|      | ||||
|     public GetAllFeaturesWithin(bbox: BBox): any[][]{ | ||||
| 
 | ||||
|     public GetAllFeaturesWithin(bbox: BBox): any[][] { | ||||
|         const self = this | ||||
|         const tiles = [] | ||||
|         Array.from(this.perLayerHierarchy.keys()) | ||||
|             .forEach(key => tiles.push(...self.GetFeaturesWithin(key, bbox))) | ||||
|         return tiles; | ||||
|     } | ||||
|      | ||||
|     public GetFeaturesWithin(layerId: string, bbox: BBox): any[][]{ | ||||
| 
 | ||||
|     public GetFeaturesWithin(layerId: string, bbox: BBox): any[][] { | ||||
|         const requestedHierarchy = this.perLayerHierarchy.get(layerId) | ||||
|         if (requestedHierarchy === undefined) { | ||||
|             console.warn("Layer ", layerId, "is not defined. Try one of ", Array.from(this.perLayerHierarchy.keys())) | ||||
|             return undefined; | ||||
|         } | ||||
|         return TileHierarchyTools.getTiles(requestedHierarchy, bbox) | ||||
|             .filter(featureSource => featureSource.features?.data !== undefined) | ||||
|             .map(featureSource => featureSource.features.data.map(fs => fs.feature)) | ||||
|     } | ||||
|      | ||||
|     public GetTilesPerLayerWithin(bbox: BBox, handleTile: (tile:  FeatureSourceForLayer & Tiled) => void){ | ||||
|        Array.from(this.perLayerHierarchy.values()).forEach(hierarchy => { | ||||
| 
 | ||||
|     public GetTilesPerLayerWithin(bbox: BBox, handleTile: (tile: FeatureSourceForLayer & Tiled) => void) { | ||||
|         Array.from(this.perLayerHierarchy.values()).forEach(hierarchy => { | ||||
|             TileHierarchyTools.getTiles(hierarchy, bbox).forEach(handleTile) | ||||
|         }) | ||||
|     } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue