forked from MapComplete/MapComplete
Performance optimazations
This commit is contained in:
parent
632e7e9f9a
commit
d2b245ab54
15 changed files with 321 additions and 214 deletions
112
Logic/FeatureSource/Actors/MetaTagRecalculator.ts
Normal file
112
Logic/FeatureSource/Actors/MetaTagRecalculator.ts
Normal 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()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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()
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue