Performance optimazations

This commit is contained in:
Pieter Vander Vennet 2022-01-26 20:47:08 +01:00
parent 632e7e9f9a
commit d2b245ab54
15 changed files with 321 additions and 214 deletions

View file

@ -0,0 +1,112 @@
import {FeatureSourceForLayer, Tiled} from "../FeatureSource";
import MetaTagging from "../../MetaTagging";
import {ElementStorage} from "../../ElementStorage";
import {ExtraFuncParams} from "../../ExtraFunctions";
import FeaturePipeline from "../FeaturePipeline";
import {BBox} from "../../BBox";
import {UIEventSource} from "../../UIEventSource";
/****
* Concerned with the logic of updating the right layer at the right time
*/
class MetatagUpdater {
public readonly neededLayerBboxes = new Map<string /*layerId*/, BBox>()
private source: FeatureSourceForLayer & Tiled;
private readonly params: ExtraFuncParams
private state: { allElements?: ElementStorage };
private readonly isDirty = new UIEventSource(false)
constructor(source: FeatureSourceForLayer & Tiled, state: { allElements?: ElementStorage }, featurePipeline: FeaturePipeline) {
this.state = state;
this.source = source;
const self = this;
this.params = {
getFeatureById(id) {
return state.allElements.ContainingFeatures.get(id)
},
getFeaturesWithin(layerId, bbox) {
// We keep track of the BBOX that this source needs
let oldBbox: BBox = self.neededLayerBboxes.get(layerId)
if (oldBbox === undefined) {
self.neededLayerBboxes.set(layerId, bbox);
} else if (!bbox.isContainedIn(oldBbox)) {
self.neededLayerBboxes.set(layerId,oldBbox.unionWith(bbox))
}
return featurePipeline.GetFeaturesWithin(layerId, bbox)
},
memberships: featurePipeline.relationTracker
}
this.isDirty.stabilized(100).addCallback(dirty => {
if(dirty){
self.updateMetaTags()
}
})
this.source.features.addCallbackAndRunD(_ => self.isDirty.setData(true))
}
public requestUpdate(){
this.isDirty.setData(true)
}
private updateMetaTags() {
const features = this.source.features.data
if (features.length === 0) {
this.isDirty.setData(false)
return
}
MetaTagging.addMetatags(
features,
this.params,
this.source.layer.layerDef,
this.state)
this.isDirty.setData(false)
}
}
export default class MetaTagRecalculator {
private _state: {
allElements?: ElementStorage
};
private _featurePipeline: FeaturePipeline;
private readonly _alreadyRegistered: Set<FeatureSourceForLayer & Tiled> = new Set<FeatureSourceForLayer & Tiled>()
private readonly _notifiers : MetatagUpdater[] = []
/**
* The meta tag recalculator receives tiles of layers.
* It keeps track of which sources have had their share calculated, and which should be re-updated if some other data is loaded
*/
constructor(state: { allElements?: ElementStorage }, featurePipeline: FeaturePipeline) {
this._featurePipeline = featurePipeline;
this._state = state;
}
public registerSource(source: FeatureSourceForLayer & Tiled, recalculateOnEveryChange = false) {
if (source === undefined) {
return;
}
if (this._alreadyRegistered.has(source)) {
return;
}
this._alreadyRegistered.add(source)
this._notifiers.push(new MetatagUpdater(source,this._state,this._featurePipeline))
const self = this;
source.features.addCallbackAndRunD(_ => {
const layerName = source.layer.layerDef.id
for (const updater of self._notifiers ) {
const neededBbox = updater.neededLayerBboxes.get(layerName)
if(neededBbox == undefined){
continue
}
if(source.bbox === undefined || neededBbox.overlapsWith(source.bbox)){
updater.requestUpdate()
}
}
})
}
}

View file

@ -5,7 +5,6 @@ import FeatureSource, {FeatureSourceForLayer, IndexedFeatureSource, Tiled} from
import TiledFeatureSource from "./TiledFeatureSource/TiledFeatureSource";
import {UIEventSource} from "../UIEventSource";
import {TileHierarchyTools} from "./TiledFeatureSource/TileHierarchy";
import MetaTagging from "../MetaTagging";
import RememberingSource from "./Sources/RememberingSource";
import OverpassFeatureSource from "../Actors/OverpassFeatureSource";
import GeoJsonSource from "./Sources/GeoJsonSource";
@ -49,7 +48,7 @@ export default class FeaturePipeline {
private readonly overpassUpdater: OverpassFeatureSource
private state: MapState;
private readonly relationTracker: RelationsTracker
public readonly relationTracker: RelationsTracker
private readonly perLayerHierarchy: Map<string, TileHierarchyMerger>;
/**
@ -63,9 +62,7 @@ export default class FeaturePipeline {
private readonly osmSourceZoomLevel
private readonly localStorageSavers = new Map<string, SaveTileToLocalStorageActor>()
private readonly metataggingRecalculated = new UIEventSource<void>(undefined)
private readonly requestMetataggingRecalculation = new UIEventSource<Date>(undefined)
/**
* Keeps track of all raw OSM-nodes.
* Only initialized if 'type_node' is defined as layer
@ -74,7 +71,12 @@ export default class FeaturePipeline {
constructor(
handleFeatureSource: (source: FeatureSourceForLayer & Tiled) => void,
state: MapState) {
state: MapState,
options? : {
/*Used for metatagging - will receive all the sources with changeGeometry applied but without filtering*/
handleRawFeatureSource: (source: FeatureSourceForLayer) => void
}
) {
this.state = state;
const self = this
@ -102,10 +104,6 @@ export default class FeaturePipeline {
return location.zoom >= minzoom;
}
);
this.requestMetataggingRecalculation.stabilized(500).addCallbackAndRunD(_ => {
self.updateAllMetaTagging("Request stabilized")
})
const neededTilesFromOsm = this.getNeededTilesFromOsm(this.sufficientlyZoomed)
@ -115,13 +113,13 @@ export default class FeaturePipeline {
// Given a tile, wraps it and passes it on to render (handled by 'handleFeatureSource'
function patchedHandleFeatureSource(src: FeatureSourceForLayer & IndexedFeatureSource & Tiled) {
// 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, src.tileIndex,
new ChangeGeometryApplicator(src, state.changes),
self.metataggingRecalculated
)
const withChanges = new ChangeGeometryApplicator(src, state.changes);
const srcFiltered = new FilteringFeatureSource(state, src.tileIndex,withChanges)
handleFeatureSource(srcFiltered)
if(options?.handleRawFeatureSource){
options.handleRawFeatureSource(withChanges)
}
self.somethingLoaded.setData(true)
// We do not mark as visited here, this is the responsability of the code near the actual loader (e.g. overpassLoader and OSMApiFeatureLoader)
}
@ -178,11 +176,6 @@ export default class FeaturePipeline {
if (id === "current_view") {
handlePriviligedFeatureSource(state.currentView)
state.currentView.features.map(ffs => ffs[0]?.feature?.properties?.id).withEqualityStabilized((x,y) => x === y)
.addCallbackAndRunD(_ => {
self.applyMetaTags(state.currentView, <any>this.state, `currentview changed`)
}
)
continue
}
@ -324,7 +317,6 @@ export default class FeaturePipeline {
perLayerHierarchy.get(perLayer.layer.layerDef.id).registerTile(perLayer)
// AT last, we always apply the metatags whenever possible
perLayer.features.addCallbackAndRunD(feats => {
console.log("New feature for layer ", perLayer.layer.layerDef.id, ":", feats)
self.onNewDataLoaded(perLayer);
})
@ -335,13 +327,6 @@ export default class FeaturePipeline {
}}
)
// Whenever fresh data comes in, we need to update the metatagging
self.newDataLoadedSignal.stabilized(250).addCallback(src => {
self.updateAllMetaTagging(`New data loaded by ${src.name} (and stabilized)`)
})
this.runningQuery = updater.runningQuery.map(
overpass => {
console.log("FeaturePipeline: runningQuery state changed: Overpass", overpass ? "is querying," : "is idle,",
@ -354,7 +339,6 @@ export default class FeaturePipeline {
private onNewDataLoaded(src: FeatureSource){
this.newDataLoadedSignal.setData(src)
this.requestMetataggingRecalculation.setData(new Date())
}
public GetAllFeaturesWithin(bbox: BBox): any[][] {
@ -501,44 +485,4 @@ export default class FeaturePipeline {
return updater;
}
private applyMetaTags(src: FeatureSourceForLayer, state: any, reason: string) {
const self = this
if(src === undefined){
throw "Src is undefined"
}
const layerDef = src.layer.layerDef;
console.debug(`Applying metatags onto ${src.name} due to ${reason} which has ${src.features.data?.length} features`)
if(src.features.data.length == 0){
return
}
MetaTagging.addMetatags(
src.features.data,
{
memberships: this.relationTracker,
getFeaturesWithin: (layerId, bbox: BBox) => self.GetFeaturesWithin(layerId, bbox),
getFeatureById: (id: string) => self.state.allElements.ContainingFeatures.get(id)
},
layerDef,
state,
{
includeDates: true,
// We assume that the non-dated metatags are already set by the cache generator
includeNonDates: layerDef.source.geojsonSource === undefined || !layerDef.source.isOsmCacheLayer
}
)
}
public updateAllMetaTagging(reason: string) {
const self = this;
this.perLayerHierarchy.forEach(hierarchy => {
hierarchy.loadedTiles.forEach(tile => {
self.applyMetaTags(tile, <any> this.state, `${reason} (tile ${tile.tileIndex})`)
})
})
self.metataggingRecalculated.ping()
}
}