import { UIEventSource } from "../../UIEventSource" import FeatureSource, { FeatureSourceForLayer, IndexedFeatureSource, Tiled } from "../FeatureSource" import FilteredLayer from "../../../Models/FilteredLayer" import { Tiles } from "../../../Models/TileRange" import { BBox } from "../../BBox" export default class FeatureSourceMerger implements FeatureSourceForLayer, Tiled, IndexedFeatureSource { public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource< { feature: any; freshness: Date }[] >([]) public readonly name public readonly layer: FilteredLayer public readonly tileIndex: number public readonly bbox: BBox public readonly containedIds: UIEventSource> = new UIEventSource>( new Set() ) private readonly _sources: UIEventSource /** * Merges features from different featureSources for a single layer * Uses the freshest feature available in the case multiple sources offer data with the same identifier */ constructor( layer: FilteredLayer, tileIndex: number, bbox: BBox, sources: UIEventSource ) { this.tileIndex = tileIndex this.bbox = bbox this._sources = sources this.layer = layer this.name = "FeatureSourceMerger(" + layer.layerDef.id + ", " + Tiles.tile_from_index(tileIndex).join(",") + ")" const self = this const handledSources = new Set() sources.addCallbackAndRunD((sources) => { let newSourceRegistered = false for (let i = 0; i < sources.length; i++) { let source = sources[i] if (handledSources.has(source)) { continue } handledSources.add(source) newSourceRegistered = true source.features.addCallback(() => { self.Update() }) if (newSourceRegistered) { self.Update() } } }) } private Update() { let somethingChanged = false const all: Map = new Map< string, { feature: any; freshness: Date } >() // We seed the dictionary with the previously loaded features const oldValues = this.features.data ?? [] for (const oldValue of oldValues) { all.set(oldValue.feature.id, oldValue) } for (const source of this._sources.data) { if (source?.features?.data === undefined) { continue } for (const f of source.features.data) { const id = f.feature.properties.id if (!all.has(id)) { // This is a new feature somethingChanged = true all.set(id, f) continue } // This value has been seen already, either in a previous run or by a previous datasource // Let's figure out if something changed const oldV = all.get(id) if (oldV.freshness < f.freshness) { // Jup, this feature is fresher all.set(id, f) somethingChanged = true } } } if (!somethingChanged) { // We don't bother triggering an update return } const newList = [] all.forEach((value, _) => { newList.push(value) }) this.containedIds.setData(new Set(all.keys())) this.features.setData(newList) } }