More work on splitting roads, WIP; refactoring tests

This commit is contained in:
Pieter Vander Vennet 2021-09-22 05:02:09 +02:00
parent e374bb355c
commit 1f93923820
62 changed files with 1163 additions and 823 deletions

View file

@ -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)
})
}